query("SELECT id, latitude, longitude FROM penduduk_miskin"); $updated = 0; while ($pm = $allPm->fetch_assoc()) { $prox = hitungProximity($db, (float)$pm['latitude'], (float)$pm['longitude']); $statusP = $prox['status']; $riId = $prox['rumah_ibadah_id']; $stmt = $db->prepare( "UPDATE penduduk_miskin SET status_proximity=?, rumah_ibadah_id=? WHERE id=?" ); $stmt->bind_param('sii', $statusP, $riId, $pm['id']); $stmt->execute(); $updated++; } sendSuccess(['updated' => $updated], "Proximity $updated data diperbarui"); } switch ($method) { // ── GET ─────────────────────────────────────────────────────────── case 'GET': if ($id > 0) { $stmt = $db->prepare( "SELECT pm.*, ri.nama AS nama_rumah_ibadah, ri.jenis AS jenis_rumah_ibadah FROM penduduk_miskin pm LEFT JOIN rumah_ibadah ri ON ri.id = pm.rumah_ibadah_id WHERE pm.id = ?" ); $stmt->bind_param('i', $id); $stmt->execute(); $row = $stmt->get_result()->fetch_assoc(); if (!$row) sendError('Data tidak ditemukan', 404); $row['latitude'] = (float)$row['latitude']; $row['longitude'] = (float)$row['longitude']; // Ambil ringkasan log bantuan $stmtLog = $db->prepare( "SELECT tipe_bantuan, COUNT(*) AS jumlah FROM log_bantuan WHERE penduduk_miskin_id = ? GROUP BY tipe_bantuan" ); $stmtLog->bind_param('i', $id); $stmtLog->execute(); $logs = []; $resLog = $stmtLog->get_result(); while ($l = $resLog->fetch_assoc()) $logs[] = $l; $row['ringkasan_bantuan'] = $logs; sendSuccess($row); } else { // GET semua + proximity recalculation cache $sql = "SELECT pm.id, pm.nama, pm.no_wa, pm.nik, pm.alamat, pm.jumlah_anggota, pm.latitude, pm.longitude, pm.status_proximity, pm.rumah_ibadah_id, ri.nama AS nama_rumah_ibadah, -- hitung berapa kali dapat bantuan (SELECT COUNT(*) FROM log_bantuan lb WHERE lb.penduduk_miskin_id = pm.id) AS total_bantuan, (SELECT COUNT(*) FROM log_bantuan lb WHERE lb.penduduk_miskin_id = pm.id AND lb.tipe_bantuan = 'Pemberdayaan') AS bantuan_pemberdayaan, (SELECT COUNT(*) FROM log_bantuan lb WHERE lb.penduduk_miskin_id = pm.id AND lb.tipe_bantuan = 'Konsumtif') AS bantuan_konsumtif FROM penduduk_miskin pm LEFT JOIN rumah_ibadah ri ON ri.id = pm.rumah_ibadah_id ORDER BY pm.created_at DESC"; $result = $db->query($sql); $rows = []; while ($r = $result->fetch_assoc()) { $r['latitude'] = (float)$r['latitude']; $r['longitude'] = (float)$r['longitude']; $r['total_bantuan'] = (int)$r['total_bantuan']; $r['bantuan_pemberdayaan'] = (int)$r['bantuan_pemberdayaan']; $r['bantuan_konsumtif'] = (int)$r['bantuan_konsumtif']; $rows[] = $r; } sendSuccess($rows); } break; // ── POST ────────────────────────────────────────────────────────── case 'POST': $body = getBody(); foreach (['nama', 'latitude', 'longitude'] as $f) { if (empty($body[$f]) && $body[$f] !== 0) sendError("Field '$f' wajib diisi"); } // Hitung proximity ke semua rumah ibadah $proximity = hitungProximity($db, $body['latitude'], $body['longitude']); $stmt = $db->prepare( "INSERT INTO penduduk_miskin (nama, no_wa, nik, alamat, jumlah_anggota, latitude, longitude, status_proximity, rumah_ibadah_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $jumlah = isset($body['jumlah_anggota']) ? (int)$body['jumlah_anggota'] : 1; $statusP = $proximity['status']; $riId = $proximity['rumah_ibadah_id']; $stmt->bind_param( 'ssssiddsi', $body['nama'], $body['no_wa'], $body['nik'], $body['alamat'], $jumlah, $body['latitude'], $body['longitude'], $statusP, $riId ); executeOrFail($stmt, 'Gagal menyimpan data penduduk miskin'); $newId = $db->insert_id; sendSuccess( ['id' => $newId, 'proximity' => $proximity], 'Penduduk Miskin berhasil disimpan', 201 ); break; // ── PUT ─────────────────────────────────────────────────────────── case 'PUT': if ($id <= 0) sendError('ID wajib disertakan'); $body = getBody(); $proximity = hitungProximity($db, $body['latitude'], $body['longitude']); $statusP = $proximity['status']; $riId = $proximity['rumah_ibadah_id']; $jumlah = isset($body['jumlah_anggota']) ? (int)$body['jumlah_anggota'] : 1; $stmt = $db->prepare( "UPDATE penduduk_miskin SET nama=?, no_wa=?, nik=?, alamat=?, jumlah_anggota=?, latitude=?, longitude=?, status_proximity=?, rumah_ibadah_id=? WHERE id=?" ); $stmt->bind_param( 'ssssiddsii', $body['nama'], $body['no_wa'], $body['nik'], $body['alamat'], $jumlah, $body['latitude'], $body['longitude'], $statusP, $riId, $id ); executeOrFail($stmt, 'Gagal memperbarui data penduduk miskin'); sendSuccess(['proximity' => $proximity], 'Data berhasil diperbarui'); break; // ── DELETE ──────────────────────────────────────────────────────── case 'DELETE': if ($id <= 0) sendError('ID wajib disertakan'); $stmt = $db->prepare("DELETE FROM penduduk_miskin WHERE id=?"); $stmt->bind_param('i', $id); executeOrFail($stmt, 'Gagal menghapus data penduduk miskin'); $stmt->affected_rows > 0 ? sendSuccess(null, 'Data berhasil dihapus') : sendError('Data tidak ditemukan', 404); break; default: sendError('Method tidak diizinkan', 405); } // ───────────────────────────────────────────────────────────────────────────── // HELPER: Hitung proximity penduduk miskin terhadap semua rumah ibadah // Menggunakan Haversine formula di PHP untuk validasi server-side // ───────────────────────────────────────────────────────────────────────────── function hitungProximity(mysqli $db, float $lat, float $lng): array { $result = $db->query( "SELECT id, nama, latitude, longitude, radius_meter FROM rumah_ibadah" ); $status = 'luar_radius'; $riId = null; $riNama = null; $minDistAll = PHP_INT_MAX; // jarak ke RI terdekat (apapun statusnya) $minDistIn = PHP_INT_MAX; // jarak ke RI terdekat yang dalam radius while ($ri = $result->fetch_assoc()) { $dist = haversine($lat, $lng, (float)$ri['latitude'], (float)$ri['longitude']); // Lacak jarak terdekat secara keseluruhan (untuk field jarak_meter) if ($dist < $minDistAll) { $minDistAll = $dist; } // Cek apakah titik ini masuk dalam radius RI ini if ($dist <= (int)$ri['radius_meter'] && $dist < $minDistIn) { $minDistIn = $dist; $status = 'dalam_radius'; $riId = (int)$ri['id']; $riNama = $ri['nama']; } } return [ 'status' => $status, 'rumah_ibadah_id' => $riId, 'nama_rumah_ibadah' => $riNama, 'jarak_meter' => round($minDistAll, 2) ]; } function haversine(float $lat1, float $lng1, float $lat2, float $lng2): float { $R = 6371000; // radius bumi dalam meter $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)); }