diff --git a/frontend/src/pages/TourDetailPage.tsx b/frontend/src/pages/TourDetailPage.tsx index 475d448..1db666d 100644 --- a/frontend/src/pages/TourDetailPage.tsx +++ b/frontend/src/pages/TourDetailPage.tsx @@ -640,7 +640,7 @@ export const TourDetailPage = ({ const coordsString = `${p1.longitude},${p1.latitude};${p2.longitude},${p2.latitude}`; segmentPromises.push( - fetch(`https://router.project-osrm.org/route/v1/${travelMode}/${coordsString}?overview=full&geometries=geojson&alternatives=true`) + fetch(`https://router.project-osrm.org/route/v1/${travelMode}/${coordsString}?overview=full&geometries=geojson&alternatives=3`) // Yêu cầu tối đa 5 phương án thay thế cho mỗi phân đoạn .then(res => { if (!res.ok) throw new Error(`OSRM API error: ${res.status}`); return res.json(); @@ -674,14 +674,24 @@ export const TourDetailPage = ({ const overallAlternativeRoutes: OSRMRoute[] = []; // Limit overall alternatives based on the number of alternatives for the first segment, up to 3 - const maxOverallAlternatives = Math.min(3, allSegmentAlternatives[0]?.length || 1); + const maxOverallAlternativesToGenerate = 3; // Số lượng lộ trình tổng thể muốn tạo - for (let altIdx = 0; altIdx < maxOverallAlternatives; altIdx++) { - const selectedSegmentIndices: number[] = allSegmentAlternatives.map((_, segIdx) => - segIdx === 0 ? altIdx : 0 // Use altIdx for first segment, 0 for others + // Heuristic để tạo các lộ trình tổng thể đa dạng hơn: + // 1. Lộ trình chính (fastest/default cho tất cả các phân đoạn) + const primarySegmentIndices = allSegmentAlternatives.map(() => 0); + const primaryCombinedRoute = combineSegmentRoutes(allSegmentAlternatives, primarySegmentIndices); + if (primaryCombinedRoute) { + overallAlternativeRoutes.push(primaryCombinedRoute); + } + + // 2. Các lộ trình thay thế: Thử kết hợp các phương án thay thế từ các phân đoạn + // Ví dụ: Lấy phương án thứ N của mỗi phân đoạn (nếu có), hoặc phương án 0 nếu không có + for (let altChoice = 1; altChoice < maxOverallAlternativesToGenerate; altChoice++) { + const selectedSegmentIndices: number[] = allSegmentAlternatives.map(segmentAlts => + Math.min(altChoice, segmentAlts.length - 1) // Chọn phương án thứ 'altChoice', hoặc phương án cuối cùng nếu không đủ ); const combinedRoute = combineSegmentRoutes(allSegmentAlternatives, selectedSegmentIndices); - if (combinedRoute) { + if (combinedRoute && !overallAlternativeRoutes.some(r => JSON.stringify(r.geometry.coordinates) === JSON.stringify(combinedRoute.geometry.coordinates))) { overallAlternativeRoutes.push(combinedRoute); } } @@ -711,6 +721,33 @@ export const TourDetailPage = ({ } }, [selectedRouteIndex, routes]); + // Tính toán thông tin hiển thị cho lộ trình đang chọn để đề xuất cho người dùng + const selectedRouteInfo = useMemo(() => { + if (!routes || routes.length === 0 || !routes[selectedRouteIndex]) return null; + const r = routes[selectedRouteIndex]; + + // Định dạng thời gian di chuyển + const duration = r.duration; + const hours = Math.floor(duration / 3600); + const minutes = Math.round((duration % 3600) / 60); + const durationStr = hours > 0 ? `${hours}h${minutes}p` : `${minutes}p`; + + // Xác định nhãn đề xuất: Index 0 thường là lộ trình tối ưu nhất của OSRM (thông dụng nhất) + // Kiểm tra thêm nếu đây là lộ trình ngắn nhất trong các phương án + const isShortest = routes.length > 1 && r.distance === Math.min(...routes.map(rt => rt.distance)); + + let label = "Lộ trình"; + if (selectedRouteIndex === 0) label = "Đề xuất"; + else if (isShortest) label = "Ngắn nhất"; + else label = `Lựa chọn ${selectedRouteIndex + 1}`; + + return { + distance: (r.distance / 1000).toFixed(1), + duration: durationStr, + label + }; + }, [routes, selectedRouteIndex]); + useEffect(() => { if (initialViewState) { setMapCenter(initialViewState.center); @@ -1391,6 +1428,22 @@ export const TourDetailPage = ({ )} + + {/* Overlay thông tin lộ trình đề xuất (Lộ trình thông dụng nhất) */} + {selectedRouteInfo && drivingRoute.length > 0 && ( +