Files
HouseHub/modules/voyage/api.php
T
FeFeClochette f748330a1b
Deploy HouseHub / deploy (push) Successful in 2s
gps expense
2026-07-29 14:58:50 +02:00

234 lines
7.6 KiB
PHP

<?php
// modules/voyage/api.php
// Toll cost estimator API for HouseHub
require_once dirname(__DIR__, 2) . '/includes/auth.php';
require_login();
header('Content-Type: application/json; charset=utf-8');
$action = $_GET['action'] ?? '';
if ($action !== 'estimate') {
echo json_encode(['ok' => false, 'error' => 'Action inconnue']);
exit;
}
// --- Read POST body ---
$body = file_get_contents('php://input');
$params = json_decode($body, true);
if (!$params || empty($params['stops']) || count($params['stops']) < 2) {
echo json_encode(['ok' => false, 'error' => 'Paramètres invalides (stops requis, min 2)']);
exit;
}
$stops = $params['stops'];
$fuelL100 = (float)($params['fuel_l100'] ?? 7);
$fuelPrice= (float)($params['fuel_price'] ?? 1.85);
// --- Load pre-indexed PHP database ---
$tollsDataPath = __DIR__ . '/data/tolls.data.php';
$gpsPath = __DIR__ . '/data/toll_gps.json';
if (!file_exists($tollsDataPath)) {
echo json_encode(['ok' => false, 'error' => 'Base de péages introuvable']);
exit;
}
// Grâce à OPcache, le fichier est exécuté et stocké en RAM partagée
$tollIndex = require $tollsDataPath;
$gpsData = [];
if (file_exists($gpsPath)) {
$gpsData = json_decode(file_get_contents($gpsPath), true) ?? [];
}
// --- Helper: great-circle distance (km) ---
function haversine(float $lat1, float $lng1, float $lat2, float $lng2): float {
$R = 6371.0;
$dLat = deg2rad($lat2 - $lat1);
$dLng = deg2rad($lng2 - $lng1);
$a = sin($dLat/2)**2 + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLng/2)**2;
return $R * 2 * atan2(sqrt($a), sqrt(1 - $a));
}
// --- Find plazas near a coordinate (within $maxKm) ---
// Returns array of [plaza_name => distance_km]
function plazasNear(array $gpsData, float $lat, float $lng, float $maxKm): array {
$result = [];
foreach ($gpsData as $name => $info) {
$d = haversine($lat, $lng, $info['lat'], $info['lng']);
if ($d <= $maxKm) {
$result[$name] = $d;
}
}
asort($result);
return $result;
}
// --- Look up toll price for (op, entry, exit) with fallback ---
function lookupToll(array $tollIndex, string $op, string $entry, string $exit): ?float {
// Direct lookup
if (isset($tollIndex[$op][$entry][$exit])) {
return $tollIndex[$op][$entry][$exit];
}
// Reversed
if (isset($tollIndex[$op][$exit][$entry])) {
return $tollIndex[$op][$exit][$entry];
}
return null;
}
// --- OSRM route distance ---
function getRouteDistanceKm(float $lat1, float $lng1, float $lat2, float $lng2): ?float {
$url = sprintf(
'https://router.project-osrm.org/route/v1/driving/%f,%f;%f,%f?overview=false',
$lng1, $lat1, $lng2, $lat2
);
$ctx = stream_context_create([
'http' => [
'timeout' => 10,
'header' => "User-Agent: HouseHub/1.0\r\n"
]
]);
$resp = @file_get_contents($url, false, $ctx);
if ($resp === false) return null;
$data = json_decode($resp, true);
if (!empty($data['routes'][0]['distance'])) {
return (float)$data['routes'][0]['distance'] / 1000.0;
}
return null;
}
// --- Main: process each consecutive stop pair ---
$segments = [];
$totalKm = 0.0;
$totalToll = 0.0;
for ($i = 0; $i < count($stops) - 1; $i++) {
$orig = $stops[$i];
$dest = $stops[$i + 1];
$oLat = (float)$orig['lat'];
$oLng = (float)$orig['lng'];
$dLat = (float)$dest['lat'];
$dLng = (float)$dest['lng'];
$fromName = $orig['name'] ?? "Étape " . ($i + 1);
$toName = $dest['name'] ?? "Étape " . ($i + 2);
// Distance via OSRM
$distKm = getRouteDistanceKm($oLat, $oLng, $dLat, $dLng);
if ($distKm === null) {
// Fallback: straight-line distance * 1.25
$distKm = haversine($oLat, $oLng, $dLat, $dLng) * 1.25;
}
$totalKm += $distKm;
// --- Find toll ---
$segToll = 0.0;
$entryPlaza = null;
$exitPlaza = null;
$tollNote = null;
if (!empty($gpsData)) {
// Find plazas near origin (80km) and destination (80km)
$nearOrig = plazasNear($gpsData, $oLat, $oLng, 80.0);
$nearDest = plazasNear($gpsData, $dLat, $dLng, 80.0);
$bestToll = null;
$bestDistSum = PHP_FLOAT_MAX;
$bestEntry = null;
$bestExit = null;
$bestOp = null;
// Try each operator
foreach ($tollIndex as $op => $opEntries) {
// Find candidate entry plazas (near origin, present in this operator)
$candidateEntries = [];
foreach ($nearOrig as $pName => $dist) {
if (isset($opEntries[$pName]) || isset(array_flip(array_keys($opEntries))[$pName])) {
// Plaza exists as entry in this operator
if (isset($opEntries[$pName])) {
$candidateEntries[$pName] = $dist;
}
}
// Also check if it's an exit plaza (reversed lookup later)
// Include any plaza from this operator that is within range
}
// Build set of all plazas for this operator
$opPlazas = [];
foreach ($opEntries as $entry => $exits) {
$opPlazas[$entry] = true;
foreach ($exits as $exit => $price) {
$opPlazas[$exit] = true;
}
}
// Filter nearOrig and nearDest to only plazas in this operator
$opNearOrig = array_intersect_key($nearOrig, $opPlazas);
$opNearDest = array_intersect_key($nearDest, $opPlazas);
if (empty($opNearOrig) || empty($opNearDest)) continue;
// Try best 5 entry and best 5 exit candidates
$topEntries = array_slice($opNearOrig, 0, 5, true);
$topExits = array_slice($opNearDest, 0, 5, true);
foreach ($topEntries as $ePlaza => $eDist) {
foreach ($topExits as $xPlaza => $xDist) {
if ($ePlaza === $xPlaza) continue;
$price = lookupToll($tollIndex, $op, $ePlaza, $xPlaza);
if ($price !== null) {
$distSum = $eDist + $xDist;
if ($distSum < $bestDistSum) {
$bestDistSum = $distSum;
$bestToll = $price;
$bestEntry = $ePlaza;
$bestExit = $xPlaza;
$bestOp = $op;
}
}
}
}
}
if ($bestToll !== null) {
$segToll = $bestToll;
$entryPlaza = $bestEntry;
$exitPlaza = $bestExit;
} else {
$tollNote = 'péages non estimés pour ce trajet';
}
} else {
$tollNote = 'base GPS des péages non disponible';
}
$totalToll += $segToll;
$seg = [
'from' => $fromName,
'to' => $toName,
'distance_km' => round($distKm, 1),
'toll' => round($segToll, 2),
];
if ($entryPlaza) $seg['entry_plaza'] = $entryPlaza;
if ($exitPlaza) $seg['exit_plaza'] = $exitPlaza;
if ($bestOp) $seg['op'] = $bestOp;
if ($tollNote) $seg['note'] = $tollNote;
$segments[] = $seg;
}
$fuelCost = round(($totalKm / 100.0) * $fuelL100 * $fuelPrice, 2);
$grandTotal = round($totalToll + $fuelCost, 2);
echo json_encode([
'ok' => true,
'segments' => $segments,
'total_km' => round($totalKm, 1),
'total_toll' => round($totalToll, 2),
'fuel_cost' => $fuelCost,
'grand_total' => $grandTotal,
], JSON_UNESCAPED_UNICODE);