161 lines
4.9 KiB
TypeScript
161 lines
4.9 KiB
TypeScript
"use client"
|
|
|
|
import { useState, useEffect } from "react"
|
|
import type { Delivery } from "../types"
|
|
|
|
// Token de acesso do Mapbox
|
|
const MAPBOX_ACCESS_TOKEN =
|
|
"pk.eyJ1IjoiYWxlaW5jb25uZXh0IiwiYSI6ImNtOGtvdzFueDBuMGUybHBvYjd4d3kyZDQifQ.MXDcXpxKAXtQkyAwv-_1tQ"
|
|
|
|
interface RouteResponse {
|
|
routes: Array<{
|
|
distance: number // em metros
|
|
duration: number // em segundos
|
|
geometry: {
|
|
coordinates: number[][]
|
|
}
|
|
}>
|
|
}
|
|
|
|
interface RealRouteData {
|
|
totalDistance: number // em km
|
|
totalDuration: number // em minutos
|
|
isLoading: boolean
|
|
error: string | null
|
|
routeCoordinates: Array<{ latitude: number; longitude: number }>
|
|
}
|
|
|
|
interface UseRealRouteDataProps {
|
|
deliveries: Delivery[]
|
|
distributionCenter: { latitude: number; longitude: number }
|
|
}
|
|
|
|
export const useRealRouteData = ({ deliveries, distributionCenter }: UseRealRouteDataProps): RealRouteData => {
|
|
const [routeData, setRouteData] = useState<RealRouteData>({
|
|
totalDistance: 0,
|
|
totalDuration: 0,
|
|
isLoading: false,
|
|
error: null,
|
|
routeCoordinates: [],
|
|
})
|
|
|
|
useEffect(() => {
|
|
if (deliveries.length === 0) return
|
|
|
|
calculateRealRoute()
|
|
}, [deliveries])
|
|
|
|
const calculateHaversineDistance = (lat1: number, lon1: number, lat2: number, lon2: number): number => {
|
|
const R = 6371 // Raio da Terra em km
|
|
const dLat = ((lat2 - lat1) * Math.PI) / 180
|
|
const dLon = ((lon2 - lon1) * Math.PI) / 180
|
|
const a =
|
|
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
Math.cos((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2)
|
|
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
|
|
return R * c
|
|
}
|
|
|
|
const calculateSimpleRoute = (
|
|
startPoint: { latitude: number; longitude: number },
|
|
deliveryPoints: Array<{ latitude: number; longitude: number; id: string }>,
|
|
): RealRouteData => {
|
|
let totalDistance = 0
|
|
let currentPoint = startPoint
|
|
|
|
// Calcular distância entre pontos consecutivos
|
|
deliveryPoints.forEach((point) => {
|
|
const distance = calculateHaversineDistance(
|
|
currentPoint.latitude,
|
|
currentPoint.longitude,
|
|
point.latitude,
|
|
point.longitude,
|
|
)
|
|
totalDistance += distance
|
|
currentPoint = point
|
|
})
|
|
|
|
// Estimar tempo baseado em velocidade média urbana (25 km/h)
|
|
const estimatedDuration = (totalDistance / 25) * 60 // minutos
|
|
|
|
return {
|
|
totalDistance,
|
|
totalDuration: estimatedDuration,
|
|
isLoading: false,
|
|
error: null,
|
|
routeCoordinates: [startPoint, ...deliveryPoints],
|
|
}
|
|
}
|
|
|
|
const calculateRealRoute = async () => {
|
|
setRouteData((prev) => ({ ...prev, isLoading: true, error: null }))
|
|
|
|
try {
|
|
// Filtrar entregas com coordenadas válidas
|
|
const validDeliveries = deliveries
|
|
.filter((delivery) => delivery.coordinates || (delivery.lat && delivery.lng))
|
|
.sort((a, b) => a.deliverySeq - b.deliverySeq)
|
|
.map((delivery) => ({
|
|
id: delivery.id,
|
|
latitude: delivery.coordinates?.latitude || delivery.lat!,
|
|
longitude: delivery.coordinates?.longitude || delivery.lng!,
|
|
}))
|
|
|
|
if (validDeliveries.length === 0) {
|
|
throw new Error("Nenhuma entrega com coordenadas válidas")
|
|
}
|
|
|
|
// Tentar usar Mapbox Directions API
|
|
try {
|
|
const coordinates = [
|
|
[distributionCenter.longitude, distributionCenter.latitude],
|
|
...validDeliveries.map((point) => [point.longitude, point.latitude]),
|
|
]
|
|
|
|
const coordinatesString = coordinates.map((coord) => `${coord[0]},${coord[1]}`).join(";")
|
|
|
|
const url =
|
|
`https://api.mapbox.com/directions/v5/mapbox/driving/${coordinatesString}?` +
|
|
`geometries=geojson&` +
|
|
`overview=full&` +
|
|
`steps=true&` +
|
|
`access_token=${MAPBOX_ACCESS_TOKEN}`
|
|
|
|
const response = await fetch(url)
|
|
const data: RouteResponse = await response.json()
|
|
|
|
if (data.routes && data.routes.length > 0) {
|
|
const route = data.routes[0]
|
|
|
|
setRouteData({
|
|
totalDistance: route.distance / 1000, // converter para km
|
|
totalDuration: route.duration / 60, // converter para minutos
|
|
isLoading: false,
|
|
error: null,
|
|
routeCoordinates: route.geometry.coordinates.map((coord) => ({
|
|
latitude: coord[1],
|
|
longitude: coord[0],
|
|
})),
|
|
})
|
|
return
|
|
}
|
|
} catch (mapboxError) {
|
|
console.warn("Erro na API do Mapbox, usando cálculo simples:", mapboxError)
|
|
}
|
|
|
|
// Fallback para cálculo simples
|
|
const simpleRoute = calculateSimpleRoute(distributionCenter, validDeliveries)
|
|
setRouteData(simpleRoute)
|
|
} catch (error) {
|
|
console.error("Erro ao calcular rota:", error)
|
|
setRouteData((prev) => ({
|
|
...prev,
|
|
isLoading: false,
|
|
error: error instanceof Error ? error.message : "Erro desconhecido",
|
|
}))
|
|
}
|
|
}
|
|
|
|
return routeData
|
|
}
|