fix(travel): revert global geometric extraction and add surgical fallback for missing origin systems
This commit is contained in:
@@ -102,12 +102,15 @@ 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) {
|
||||||
// Système fermé : 1 réponse avec entranceToll + exitToll
|
if (passages.length === 2) {
|
||||||
|
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;
|
||||||
@@ -119,47 +122,101 @@ async function getUlysRate(vehicleCategory, passages) {
|
|||||||
// ─────────────────────────────────────────────
|
// ─────────────────────────────────────────────
|
||||||
async function calculateTollCost(encodedPolyline, vehicleCategory) {
|
async function calculateTollCost(encodedPolyline, vehicleCategory) {
|
||||||
try {
|
try {
|
||||||
// 1. Demander à Ulys les gares sur le tracé
|
const polylineCoords = polylineLib.decode(encodedPolyline, 5);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
if (legsData && Array.isArray(legsData.features) && legsData.features.length > 0) {
|
const items = Array.isArray(legsData) ? legsData : (legsData.features || []);
|
||||||
// Extraire les gares dans l'ordre du tracé
|
let tollGates = [];
|
||||||
const tollGates = [];
|
|
||||||
for (const feature of legsData.features) {
|
|
||||||
const props = feature.properties || {};
|
|
||||||
|
|
||||||
// La réponse Ulys peut utiliser différents noms de champs
|
if (items.length > 0) {
|
||||||
// On cherche l'identifiant de la gare dans tous les champs connus
|
for (const item of items) {
|
||||||
const id =
|
const pm = item.Placemark || item.placemark || {};
|
||||||
props.id_gare ||
|
const tags = pm.Tags || pm.tags || {};
|
||||||
props.idGare ||
|
const props = pm.properties || {};
|
||||||
props.id ||
|
|
||||||
props.gareId ||
|
|
||||||
props.gare_id ||
|
|
||||||
props.tollStationId;
|
|
||||||
|
|
||||||
if (!id) continue;
|
let rawId = tags.ID_PEAGE || tags.id_peage || (pm.Code || '').split('_')[0] || props.id_gare || props.idGare || props.id;
|
||||||
const idStr = String(id);
|
if (!rawId) continue;
|
||||||
|
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: props.nom || props.name || props.label || idStr,
|
name: pm.Preview || pm.preview || tags.nom || 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`);
|
||||||
if (tollGates.length === 0) return 0;
|
const baseToll = await _computeTollFromGates(tollGates, vehicleCategory);
|
||||||
|
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;
|
||||||
|
|||||||
Reference in New Issue
Block a user