diff --git a/em2rp/functions/src/travel.js b/em2rp/functions/src/travel.js index 9ec87ee..4f2b99a 100644 --- a/em2rp/functions/src/travel.js +++ b/em2rp/functions/src/travel.js @@ -49,14 +49,8 @@ function loadTollStations() { // ───────────────────────────────────────────── async function getUlysTollLegs(encodedPolyline) { 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 body = JSON.stringify(polyline6); + const body = JSON.stringify(encodedPolyline); const res = await axios.post(url, body, { headers: { 'Content-Type': 'application/json', @@ -102,14 +96,11 @@ async function getUlysRate(vehicleCategory, passages) { ); const data = res.data; if (Array.isArray(data) && data.length > 0) { - 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; - const total = data.reduce((sum, d) => sum + (d.price || 0), 0); - return total > 0 ? total : null; - } + // Système fermé : 1 réponse avec entranceToll + exitToll + 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); + return total > 0 ? total : null; } return null; } catch (e) { @@ -122,101 +113,47 @@ async function getUlysRate(vehicleCategory, passages) { // ───────────────────────────────────────────── async function calculateTollCost(encodedPolyline, vehicleCategory) { try { - const polylineCoords = polylineLib.decode(encodedPolyline, 5); - - // Appel direct à Ulys avec le segment entier (pas de sous-échantillonnage pour éviter de rater des gares) + // 1. Demander à Ulys les gares sur le tracé const legsData = await getUlysTollLegs(encodedPolyline); - const items = Array.isArray(legsData) ? legsData : (legsData.features || []); - let tollGates = []; + if (legsData && Array.isArray(legsData.features) && legsData.features.length > 0) { + // Extraire les gares dans l'ordre du tracé + const tollGates = []; + for (const feature of legsData.features) { + const props = feature.properties || {}; - if (items.length > 0) { - for (const item of items) { - const pm = item.Placemark || item.placemark || {}; - const tags = pm.Tags || pm.tags || {}; - const props = pm.properties || {}; + // La réponse Ulys peut utiliser différents noms de champs + // On cherche l'identifiant de la gare dans tous les champs connus + const id = + props.id_gare || + 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 (!rawId) continue; - const idStr = String(rawId).replace(/\D/g, ''); + if (!id) continue; + const idStr = String(id); if (idStr.length < 5) continue; - if (!tollGates.find((g) => g.id === idStr)) { - tollGates.push({ - id: idStr, - operatorId: idStr.substring(0, 2), - tollId: idStr.substring(2, 5), - 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; - } - } - } + tollGates.push({ + id: idStr, + operatorId: idStr.substring(0, 2), + tollId: idStr.substring(2, 5), + name: props.nom || props.name || props.label || idStr, + }); } + + logger.info(`[Travel] Ulys /legs found ${tollGates.length} toll gates`); + if (tollGates.length === 0) return 0; + + // Greedy: trouver les segments fermés + barrières ouvertes + return await _computeTollFromGates(tollGates, vehicleCategory); } - 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`); - const baseToll = await _computeTollFromGates(tollGates, vehicleCategory); - return baseToll + missingSystemPrice; - + // 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) { logger.error('[Travel] calculateTollCost error:', e); return 0;