146 lines
4.4 KiB
PHP
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'],
|
|
]);
|
|
}
|
|
}
|