Files

146 lines
4.4 KiB
PHP

<?php
/**
* Menghitung jarak antara dua koordinat menggunakan formula Haversine.
* @param float $lat1 Latitude titik pertama
* @param float $lon1 Longitude titik pertama
* @param float $lat2 Latitude titik kedua
* @param float $lon2 Longitude titik kedua
* @return float Jarak dalam satuan meter
*/
function calculateHaversineDistance($lat1, $lon1, $lat2, $lon2) {
$earthRadius = 6371000; // Radius bumi dalam meter
$latFrom = deg2rad($lat1);
$lonFrom = deg2rad($lon1);
$latTo = deg2rad($lat2);
$lonTo = deg2rad($lon2);
$latDelta = $latTo - $latFrom;
$lonDelta = $lonTo - $lonFrom;
$angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) +
cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
return $angle * $earthRadius;
}
function calculateOsrmRouteDistance($lat1, $lon1, $lat2, $lon2): ?float
{
$coordinates = sprintf(
'%.8F,%.8F;%.8F,%.8F',
$lon1,
$lat1,
$lon2,
$lat2
);
$url = 'https://router.project-osrm.org/route/v1/driving/' . $coordinates . '?overview=false&alternatives=false&steps=false';
$ch = curl_init($url);
if ($ch === false) {
return null;
}
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 4,
CURLOPT_TIMEOUT => 8,
CURLOPT_USERAGENT => 'WebGIS-Peduli/1.0',
]);
$response = curl_exec($ch);
$httpCode = (int)curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($response === false || $httpCode !== 200) {
return null;
}
$json = json_decode($response, true);
if (!is_array($json) || ($json['code'] ?? '') !== 'Ok') {
return null;
}
$distance = $json['routes'][0]['distance'] ?? null;
return is_numeric($distance) ? (float)$distance : null;
}
function calculateRoadDistance($lat1, $lon1, $lat2, $lon2): ?float
{
return calculateOsrmRouteDistance((float)$lat1, (float)$lon1, (float)$lat2, (float)$lon2);
}
function ensurePendudukDistanceColumns(PDO $pdo): void
{
$stmt = $pdo->query("SHOW COLUMNS FROM penduduk_miskin LIKE 'jarak_rute_m'");
if ($stmt->fetch() === false) {
$pdo->exec("ALTER TABLE penduduk_miskin ADD COLUMN jarak_rute_m DECIMAL(10, 2) NULL AFTER jarak_m");
}
}
function calculatePendudukCoverage(PDO $pdo, float $lat, float $lng): array
{
$stmtRi = $pdo->query("SELECT id, lat, lng, radius FROM rumah_ibadah");
$rumahIbadah = $stmtRi->fetchAll(PDO::FETCH_ASSOC);
$nearest = null;
$nearestCovered = null;
foreach ($rumahIbadah as $ri) {
$distance = calculateHaversineDistance($lat, $lng, (float)$ri['lat'], (float)$ri['lng']);
$result = [
'ibadah_id' => (int)$ri['id'],
'ri_lat' => (float)$ri['lat'],
'ri_lng' => (float)$ri['lng'],
'jarak_m' => round($distance, 2),
'distance' => $distance,
];
if ($nearest === null || $distance < $nearest['distance']) {
$nearest = $result;
}
if ($distance <= (float)$ri['radius'] && ($nearestCovered === null || $distance < $nearestCovered['distance'])) {
$nearestCovered = $result;
}
}
$selected = $nearestCovered ?? $nearest;
$routeDistance = null;
if ($selected !== null) {
$routeDistance = calculateRoadDistance($lat, $lng, $selected['ri_lat'], $selected['ri_lng']);
}
return [
'ibadah_id' => $selected['ibadah_id'] ?? null,
'jarak_m' => $selected['jarak_m'] ?? null,
'jarak_rute_m' => $routeDistance !== null ? round($routeDistance, 2) : null,
'is_blank_spot' => $nearestCovered === null ? 1 : 0,
];
}
function recalculateAllPendudukCoverage(PDO $pdo): void
{
ensurePendudukDistanceColumns($pdo);
$stmt = $pdo->query("SELECT id, lat, lng FROM penduduk_miskin");
$penduduk = $stmt->fetchAll(PDO::FETCH_ASSOC);
$update = $pdo->prepare("
UPDATE penduduk_miskin
SET ibadah_id = ?, jarak_m = ?, jarak_rute_m = ?, is_blank_spot = ?
WHERE id = ?
");
foreach ($penduduk as $row) {
$coverage = calculatePendudukCoverage($pdo, (float)$row['lat'], (float)$row['lng']);
$update->execute([
$coverage['ibadah_id'],
$coverage['jarak_m'],
$coverage['jarak_rute_m'],
$coverage['is_blank_spot'],
$row['id'],
]);
}
}