Initial commit WebGIS Poverty Mapping
This commit is contained in:
@@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
error_reporting(0);
|
||||||
|
ini_set('display_errors', 0);
|
||||||
|
header('Content-Type: application/json; charset=UTF-8');
|
||||||
|
include 'koneksi.php';
|
||||||
|
if (!$conn) {
|
||||||
|
echo json_encode(array("status" => "error", "message" => "Gagal konek DB"));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
$res = array("ibadah" => array(), "penduduk" => array());
|
||||||
|
$qI = mysqli_query($conn, "SELECT id, nama, latitude, longitude, radius FROM tabel_ibadah");
|
||||||
|
if($qI){
|
||||||
|
while($r = mysqli_fetch_assoc($qI)) {
|
||||||
|
$res["ibadah"][] = array(
|
||||||
|
"id" => (int)$r['id'],
|
||||||
|
"nama_ibadah" => $r['nama'],
|
||||||
|
"latitude" => (float)$r['latitude'],
|
||||||
|
"longitude" => (float)$r['longitude'],
|
||||||
|
"radius" => (int)$r['radius']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$qP = mysqli_query($conn, "SELECT id, nama, latitude, longitude, status_warna FROM tabel_penduduk");
|
||||||
|
if($qP){
|
||||||
|
while($r = mysqli_fetch_assoc($qP)) {
|
||||||
|
$res["penduduk"][] = array(
|
||||||
|
"id" => (int)$r['id'],
|
||||||
|
"nama_penduduk" => $r['nama'],
|
||||||
|
"latitude" => (float)$r['latitude'],
|
||||||
|
"longitude" => (float)$r['longitude'],
|
||||||
|
"status_warna" => $r['status_warna']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo json_encode($res);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
if (isset($_POST['type']) && $_POST['type'] === 'ibadah') {
|
||||||
|
$nama = mysqli_real_escape_string($conn, $_POST['nama_ibadah']);
|
||||||
|
$lat = (float)$_POST['latitude'];
|
||||||
|
$lng = (float)$_POST['longitude'];
|
||||||
|
$rad = (int)$_POST['radius'];
|
||||||
|
$sql = "INSERT INTO tabel_ibadah (nama, latitude, longitude, radius) VALUES ('$nama', $lat, $lng, $rad)";
|
||||||
|
if (mysqli_query($conn, $sql)) {
|
||||||
|
echo json_encode(array("status" => "success", "message" => "Berhasil Simpan"));
|
||||||
|
} else {
|
||||||
|
echo json_encode(array("status" => "error", "message" => mysqli_error($conn)));
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if (isset($_POST['type']) && $_POST['type'] === 'penduduk') {
|
||||||
|
$nama = mysqli_real_escape_string($conn, $_POST['nama_penduduk']);
|
||||||
|
$lat = (float)$_POST['latitude'];
|
||||||
|
$lng = (float)$_POST['longitude'];
|
||||||
|
$sql = "INSERT INTO tabel_penduduk (nama, latitude, longitude, status_warna) VALUES ('$nama', $lat, $lng, 'Merah')";
|
||||||
|
if (mysqli_query($conn, $sql)) {
|
||||||
|
echo json_encode(array("status" => "success", "message" => "Berhasil Simpan"));
|
||||||
|
} else {
|
||||||
|
echo json_encode(array("status" => "error", "message" => mysqli_error($conn)));
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if (isset($_POST['action']) && $_POST['action'] === 'update_warna') {
|
||||||
|
$id = (int)$_POST['id'];
|
||||||
|
$warna = mysqli_real_escape_string($conn, $_POST['status_warna']);
|
||||||
|
$sql = "UPDATE tabel_penduduk SET status_warna = '$warna' WHERE id = $id";
|
||||||
|
if (mysqli_query($conn, $sql)) {
|
||||||
|
echo json_encode(array("status" => "success", "message" => "Update OK"));
|
||||||
|
} else {
|
||||||
|
echo json_encode(array("status" => "error", "message" => mysqli_error($conn)));
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hapus Data
|
||||||
|
if (isset($_POST['action']) && $_POST['action'] === 'hapus') {
|
||||||
|
$id = (int)$_POST['id'];
|
||||||
|
$type = $_POST['type_hapus'];
|
||||||
|
$table = ($type === 'ibadah') ? 'tabel_ibadah' : 'tabel_penduduk';
|
||||||
|
|
||||||
|
$sql = "DELETE FROM $table WHERE id = $id";
|
||||||
|
if (mysqli_query($conn, $sql)) {
|
||||||
|
echo json_encode(array("status" => "success", "message" => "Data berhasil dihapus"));
|
||||||
|
} else {
|
||||||
|
echo json_encode(array("status" => "error", "message" => mysqli_error($conn)));
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
include 'ambil.php';
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
// Script untuk memastikan database dan tabel tersedia
|
||||||
|
$host = "127.0.0.1";
|
||||||
|
$user = "root";
|
||||||
|
$pass = "";
|
||||||
|
|
||||||
|
$conn = mysqli_connect($host, $user, $pass);
|
||||||
|
if (!$conn) {
|
||||||
|
die("Koneksi MySQL Gagal: " . mysqli_connect_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Buat Database
|
||||||
|
$sql = "CREATE DATABASE IF NOT EXISTS webgis";
|
||||||
|
if (mysqli_query($conn, $sql)) {
|
||||||
|
echo "1. Database 'webgis' SIAP.<br>";
|
||||||
|
} else {
|
||||||
|
echo "Gagal buat database: " . mysqli_error($conn) . "<br>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Pilih Database
|
||||||
|
mysqli_select_db($conn, "webgis");
|
||||||
|
|
||||||
|
// 3. Buat Tabel Ibadah
|
||||||
|
$t1 = "CREATE TABLE IF NOT EXISTS `tabel_ibadah` (
|
||||||
|
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`nama_ibadah` VARCHAR(150) NOT NULL,
|
||||||
|
`latitude` DOUBLE NOT NULL,
|
||||||
|
`longitude` DOUBLE NOT NULL,
|
||||||
|
`radius` INT(11) NOT NULL DEFAULT 500,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
)";
|
||||||
|
if (mysqli_query($conn, $t1)) {
|
||||||
|
echo "2. Tabel 'tabel_ibadah' SIAP.<br>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Buat Tabel Penduduk
|
||||||
|
$t2 = "CREATE TABLE IF NOT EXISTS `tabel_penduduk` (
|
||||||
|
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`nama_penduduk` VARCHAR(150) NOT NULL,
|
||||||
|
`latitude` DOUBLE NOT NULL,
|
||||||
|
`longitude` DOUBLE NOT NULL,
|
||||||
|
`status_warna` ENUM('Merah','Hijau') NOT NULL DEFAULT 'Merah',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
)";
|
||||||
|
if (mysqli_query($conn, $t2)) {
|
||||||
|
echo "3. Tabel 'tabel_penduduk' SIAP.<br>";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "<br><b>Selesai! Silakan coba lagi fitur Radius-nya.</b>";
|
||||||
|
?>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
include 'koneksi.php';
|
||||||
|
|
||||||
|
echo "=== KOLOM tabel_ibadah ===\n";
|
||||||
|
$r = mysqli_query($conn, "DESCRIBE tabel_ibadah");
|
||||||
|
if ($r) {
|
||||||
|
while($row = mysqli_fetch_assoc($r)) {
|
||||||
|
echo "- " . $row['Field'] . "\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "Tabel tidak ada atau error: " . mysqli_error($conn) . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n=== KOLOM tabel_penduduk ===\n";
|
||||||
|
$r2 = mysqli_query($conn, "DESCRIBE tabel_penduduk");
|
||||||
|
if ($r2) {
|
||||||
|
while($row = mysqli_fetch_assoc($r2)) {
|
||||||
|
echo "- " . $row['Field'] . "\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "Tabel tidak ada atau error: " . mysqli_error($conn) . "\n";
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
-- ================================================================
|
||||||
|
-- Analisis Radius Bantuan — DDL
|
||||||
|
-- Jalankan di database: webgis
|
||||||
|
-- ================================================================
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `tabel_ibadah` (
|
||||||
|
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`nama_ibadah` VARCHAR(150) NOT NULL,
|
||||||
|
`latitude` DOUBLE NOT NULL,
|
||||||
|
`longitude` DOUBLE NOT NULL,
|
||||||
|
`radius` INT(11) NOT NULL DEFAULT 500 COMMENT 'Radius dalam meter',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `tabel_penduduk` (
|
||||||
|
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`nama_penduduk` VARCHAR(150) NOT NULL,
|
||||||
|
`latitude` DOUBLE NOT NULL,
|
||||||
|
`longitude` DOUBLE NOT NULL,
|
||||||
|
`status_warna` ENUM('Merah','Hijau') NOT NULL DEFAULT 'Merah',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
-- Data contoh (opsional, hapus jika tidak diperlukan)
|
||||||
|
INSERT INTO `tabel_ibadah` (`nama_ibadah`, `latitude`, `longitude`, `radius`) VALUES
|
||||||
|
('Masjid Al-Hikmah', -0.0200, 109.3400, 800),
|
||||||
|
('Gereja Bethel', -0.0300, 109.3500, 600);
|
||||||
|
|
||||||
|
INSERT INTO `tabel_penduduk` (`nama_penduduk`, `latitude`, `longitude`, `status_warna`) VALUES
|
||||||
|
('Budi Santoso', -0.0210, 109.3410, 'Merah'),
|
||||||
|
('Ani Rahayu', -0.0350, 109.3600, 'Merah');
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
|
||||||
|
echo "=== CEK KONEKSI ===" . PHP_EOL;
|
||||||
|
include 'koneksi.php';
|
||||||
|
|
||||||
|
if (!$conn) {
|
||||||
|
echo "KONEKSI GAGAL: " . mysqli_connect_error() . PHP_EOL;
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
echo "Koneksi OK, class: " . get_class($conn) . PHP_EOL;
|
||||||
|
echo "DB: webgis" . PHP_EOL . PHP_EOL;
|
||||||
|
|
||||||
|
echo "=== CEK TABEL ===" . PHP_EOL;
|
||||||
|
$tables = ['tabel_ibadah', 'tabel_penduduk'];
|
||||||
|
foreach ($tables as $t) {
|
||||||
|
$r = $conn->query("SHOW TABLES LIKE '$t'");
|
||||||
|
echo "$t: " . ($r && $r->num_rows > 0 ? "ADA" : "BELUM ADA") . PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo PHP_EOL . "=== CEK PREPARE ===" . PHP_EOL;
|
||||||
|
$s = $conn->prepare("SELECT id FROM tabel_ibadah LIMIT 1");
|
||||||
|
if ($s === false) {
|
||||||
|
echo "prepare() GAGAL: " . $conn->error . PHP_EOL;
|
||||||
|
} else {
|
||||||
|
echo "prepare() OK" . PHP_EOL;
|
||||||
|
$s->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
echo PHP_EOL . "=== CEK ob_start CONFLICT ===" . PHP_EOL;
|
||||||
|
echo "ob_get_level: " . ob_get_level() . PHP_EOL;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
include 'koneksi.php';
|
||||||
|
|
||||||
|
if (isset($_GET['id'])) {
|
||||||
|
$id = mysqli_real_escape_string($conn, $_GET['id']);
|
||||||
|
$query = "DELETE FROM jalan WHERE id='$id'";
|
||||||
|
|
||||||
|
if (mysqli_query($conn, $query)) {
|
||||||
|
header("Location: index.php?status=terhapus");
|
||||||
|
} else {
|
||||||
|
echo "Gagal menghapus: " . mysqli_error($conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
include 'koneksi.php';
|
||||||
|
|
||||||
|
if (isset($_GET['id'])) {
|
||||||
|
$id = mysqli_real_escape_string($conn, $_GET['id']);
|
||||||
|
$query = "DELETE FROM parsil WHERE id='$id'";
|
||||||
|
|
||||||
|
if (mysqli_query($conn, $query)) {
|
||||||
|
header("Location: index.php?status=terhapus");
|
||||||
|
} else {
|
||||||
|
echo "Gagal menghapus: " . mysqli_error($conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
include 'koneksi.php';
|
||||||
|
|
||||||
|
if (isset($_GET['id'])) {
|
||||||
|
$id = mysqli_real_escape_string($conn, $_GET['id']);
|
||||||
|
$query = "DELETE FROM spbu WHERE id='$id'";
|
||||||
|
|
||||||
|
if (mysqli_query($conn, $query)) {
|
||||||
|
header("Location: index.php?status=terhapus");
|
||||||
|
} else {
|
||||||
|
echo "Gagal menghapus: " . mysqli_error($conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
+81
@@ -0,0 +1,81 @@
|
|||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<base target="_top">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<title>Quick Start - Leaflet</title>
|
||||||
|
|
||||||
|
<link rel="shortcut icon" type="image/x-icon" href="docs/images/favicon.ico" />
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
|
||||||
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.leaflet-container {
|
||||||
|
height: 400px;
|
||||||
|
width: 600px;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="map" style="width: 600px; height: 400px;"></div>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const map = L.map('map').setView([-0.055334, 109.349482], 13);
|
||||||
|
|
||||||
|
const tiles = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
|
maxZoom: 19,
|
||||||
|
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
const marker = L.marker([-0.055277, 109.348950]).addTo(map)
|
||||||
|
.bindPopup('<b>Hello world!</b><br />I am a popup.').openPopup();
|
||||||
|
|
||||||
|
const circle = L.circle([-0.055334, 109.349482], {
|
||||||
|
color: 'red',
|
||||||
|
fillColor: '#f03',
|
||||||
|
fillOpacity: 0.5,
|
||||||
|
radius: 500
|
||||||
|
}).addTo(map).bindPopup('I am a circle.');
|
||||||
|
|
||||||
|
const polygon = L.polygon([
|
||||||
|
[-0.055334, 109.349482],
|
||||||
|
[-0.055334, 109.349482],
|
||||||
|
[-0.055334, 109.349482]
|
||||||
|
]).addTo(map).bindPopup('I am a polygon.');
|
||||||
|
|
||||||
|
|
||||||
|
const popup = L.popup()
|
||||||
|
.setLatLng([-0.055334, 109.349482])
|
||||||
|
.setContent('I am a standalone popup.')
|
||||||
|
.openOn(map);
|
||||||
|
|
||||||
|
function onMapClick(e) {
|
||||||
|
popup
|
||||||
|
.setLatLng(e.latlng)
|
||||||
|
.setContent(`You clicked the map at ${e.latlng.toString()}`)
|
||||||
|
.openOn(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
map.on('click', onMapClick);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,348 @@
|
|||||||
|
<?php
|
||||||
|
include 'koneksi.php';
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="id">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>WebGIS Pro - Pontianak</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css"/>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body { font-family: 'Plus Jakarta Sans', sans-serif; background-color: #0b132b; margin: 0; overflow: hidden; color: #fff; }
|
||||||
|
#map { height: 100vh; width: 100%; z-index: 1; }
|
||||||
|
|
||||||
|
/* --- SIDEBAR MENU MODERN DARK BLUE --- */
|
||||||
|
.sidebar-menu {
|
||||||
|
position: absolute; top: 20px; left: 20px;
|
||||||
|
z-index: 1000; width: 260px;
|
||||||
|
background: rgba(14, 23, 44, 0.85);
|
||||||
|
backdrop-filter: blur(15px);
|
||||||
|
-webkit-backdrop-filter: blur(15px);
|
||||||
|
padding: 24px; border-radius: 20px;
|
||||||
|
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.6);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
.sidebar-title { font-size: 0.75rem; font-weight: 800; color: #5bc0be; text-transform: uppercase; letter-spacing: 2px; text-shadow: 0 0 10px rgba(91,192,190,0.3); cursor: pointer; display: flex; justify-content: space-between; align-items: center; margin-bottom: 0; transition: color 0.3s;}
|
||||||
|
.sidebar-title:hover { color: #6fffe9; }
|
||||||
|
.btn-mode-container { display: flex; flex-direction: column; gap: 12px; margin-top: 20px; }
|
||||||
|
|
||||||
|
.sidebar-menu.collapsed .btn-mode-container { display: none; margin-top: 0; }
|
||||||
|
#sidebar-icon { transition: transform 0.3s; font-size: 1rem;}
|
||||||
|
.sidebar-menu:not(.collapsed) #sidebar-icon { transform: rotate(180deg); }
|
||||||
|
|
||||||
|
.btn-mode {
|
||||||
|
border-radius: 12px; border: 1px solid rgba(255,255,255,0.05); padding: 14px 18px;
|
||||||
|
font-weight: 600; font-size: 0.9rem; text-align: left;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); color: #e0e1dd; background: rgba(255,255,255,0.03);
|
||||||
|
display: flex; align-items: center; gap: 14px; cursor: pointer;
|
||||||
|
}
|
||||||
|
.btn-mode i { font-size: 1.2rem; transition: transform 0.3s ease; }
|
||||||
|
.btn-mode:hover { background: rgba(58, 80, 107, 0.4); transform: translateX(5px); border-color: rgba(91, 192, 190, 0.3); }
|
||||||
|
.btn-mode:hover i { transform: scale(1.1); }
|
||||||
|
.btn-mode.active { background: linear-gradient(135deg, #1c2541, #3a506b); color: #ffffff; border-color: #5bc0be; box-shadow: 0 4px 15px rgba(91, 192, 190, 0.4); }
|
||||||
|
.btn-mode.active i { color: #6fffe9 !important; }
|
||||||
|
|
||||||
|
/* --- POPUP STYLING DARK THEME --- */
|
||||||
|
.leaflet-popup-content-wrapper { background: rgba(20, 30, 56, 0.95); backdrop-filter: blur(15px); border: 1px solid rgba(255,255,255,0.1); border-radius: 16px; padding: 0; overflow: hidden; box-shadow: 0 15px 35px rgba(0,0,0,0.5) !important; color: #fff;}
|
||||||
|
.leaflet-popup-tip { background: rgba(20, 30, 56, 0.95) !important; border: 1px solid rgba(255,255,255,0.1); border-top: none; border-left: none; box-shadow: 15px 15px 35px rgba(0,0,0,0.5); }
|
||||||
|
.leaflet-popup-content { margin: 0 !important; width: 320px !important; }
|
||||||
|
|
||||||
|
.leaflet-popup-close-button {
|
||||||
|
color: #fff !important; top: 12px !important; right: 12px !important;
|
||||||
|
font-size: 20px !important; font-weight: bold; z-index: 1002; transition: color 0.3s;
|
||||||
|
}
|
||||||
|
.leaflet-popup-close-button:hover { color: #5bc0be !important; }
|
||||||
|
|
||||||
|
.popup-header { background: linear-gradient(135deg, #0b132b, #1c2541); border-bottom: 1px solid rgba(255,255,255,0.1); color: #6fffe9; padding: 18px 45px 18px 20px; font-weight: 800; font-size: 0.85rem; text-transform: uppercase; position: relative; letter-spacing: 1px; }
|
||||||
|
.popup-body { padding: 20px; background: transparent; }
|
||||||
|
|
||||||
|
.form-label { font-size: 0.7rem; font-weight: 700; color: #5bc0be; text-transform: uppercase; margin-bottom: 8px; display: block; letter-spacing: 1px; }
|
||||||
|
.form-control-modern { border: 1px solid rgba(255,255,255,0.1); background: rgba(0,0,0,0.2); border-radius: 10px; padding: 12px; font-size: 0.85rem; margin-bottom: 16px; width: 100%; color: #fff; transition: border-color 0.3s; }
|
||||||
|
.form-control-modern:focus { outline: none; border-color: #5bc0be; background: rgba(0,0,0,0.4); }
|
||||||
|
.form-control-modern::placeholder { color: rgba(255,255,255,0.4); }
|
||||||
|
.form-control-modern.bg-light { background: rgba(255,255,255,0.05) !important; color: #a0aab2; border-color: rgba(255,255,255,0.05); }
|
||||||
|
|
||||||
|
/* SPBU Status Toggle */
|
||||||
|
.status-toggle { display: flex; gap: 10px; margin-bottom: 18px; }
|
||||||
|
.status-toggle input { display: none; }
|
||||||
|
.status-toggle label { flex: 1; padding: 12px; text-align: center; background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); border-radius: 10px; cursor: pointer; font-size: 0.75rem; font-weight: 700; color: #a0aab2; transition: all 0.3s; }
|
||||||
|
.status-toggle input:checked + label.lab-ya { background: rgba(91, 192, 190, 0.2); color: #6fffe9; border-color: #5bc0be; box-shadow: 0 4px 10px rgba(91, 192, 190, 0.1); }
|
||||||
|
.status-toggle input:checked + label.lab-tidak { background: rgba(220, 53, 69, 0.2); color: #ff6b6b; border-color: #dc3545; box-shadow: 0 4px 10px rgba(220, 53, 69, 0.1); }
|
||||||
|
|
||||||
|
.btn-wa { background: linear-gradient(135deg, #1c2541, #3a506b); color: #6fffe9 !important; border: 1px solid #5bc0be; border-radius: 12px; font-weight: 700; padding: 12px; text-decoration: none; display: flex; align-items: center; justify-content: center; width: 100%; transition: all 0.3s; }
|
||||||
|
.btn-wa:hover { background: #5bc0be; color: #0b132b !important; box-shadow: 0 5px 15px rgba(91,192,190,0.3); }
|
||||||
|
.btn-delete-icon { width: 48px; height: 48px; background: rgba(220,53,69,0.1); border: 1px solid #dc3545; color: #ff6b6b; border-radius: 12px; display: flex; align-items: center; justify-content: center; text-decoration: none; transition: all 0.3s; }
|
||||||
|
.btn-delete-icon:hover { background: #dc3545; color: white; box-shadow: 0 5px 15px rgba(220,53,69,0.3); }
|
||||||
|
|
||||||
|
.btn-primary { background: linear-gradient(135deg, #5bc0be, #3a506b) !important; border: none !important; color: white !important; transition: transform 0.3s, box-shadow 0.3s; }
|
||||||
|
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(91,192,190,0.4); }
|
||||||
|
.btn-success { background: linear-gradient(135deg, #27ae60, #1e8449) !important; border: none !important; color: white !important; transition: transform 0.3s, box-shadow 0.3s; }
|
||||||
|
.btn-success:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(39,174,96,0.4); }
|
||||||
|
.btn-danger { background: linear-gradient(135deg, #dc3545, #900a18) !important; border: none !important; color: white !important; transition: transform 0.3s, box-shadow 0.3s; }
|
||||||
|
.btn-danger:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(220,53,69,0.4); }
|
||||||
|
|
||||||
|
.badge.bg-primary { background: rgba(91, 192, 190, 0.2) !important; color: #6fffe9 !important; border: 1px solid #5bc0be; }
|
||||||
|
.badge.bg-success { background: rgba(39, 174, 96, 0.2) !important; color: #2ecc71 !important; border: 1px solid #27ae60; }
|
||||||
|
.badge.bg-danger { background: rgba(220, 53, 69, 0.2) !important; color: #ff6b6b !important; border: 1px solid #dc3545; }
|
||||||
|
|
||||||
|
.text-primary { color: #5bc0be !important; }
|
||||||
|
.text-success { color: #6fffe9 !important; }
|
||||||
|
.text-danger { color: #ff6b6b !important; }
|
||||||
|
|
||||||
|
select option { background: #0b132b; color: #fff; }
|
||||||
|
h6 { color: #fff; }
|
||||||
|
.text-dark-blue { color: #5bc0be; }
|
||||||
|
.small { color: #a0aab2; }
|
||||||
|
|
||||||
|
/* Layers Control Styling */
|
||||||
|
.leaflet-control-layers {
|
||||||
|
background: rgba(14, 23, 44, 0.85) !important;
|
||||||
|
backdrop-filter: blur(15px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1) !important;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
color: #e0e1dd;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-separator {
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1) !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="sidebar-menu collapsed" id="sidebar-menu">
|
||||||
|
<div class="sidebar-title" onclick="toggleSidebar()">
|
||||||
|
<span><i class="bi bi-list"></i> Menu Navigasi</span>
|
||||||
|
<i class="bi bi-chevron-down" id="sidebar-icon"></i>
|
||||||
|
</div>
|
||||||
|
<div class="btn-mode-container">
|
||||||
|
<button class="btn-mode active" id="mode-view" onclick="changeMode('view')">
|
||||||
|
<i class="bi bi-layers-half"></i> Mode Lihat
|
||||||
|
</button>
|
||||||
|
<button class="btn-mode" id="mode-spbu" onclick="changeMode('spbu')">
|
||||||
|
<i class="bi bi-geo-alt-fill text-success"></i> Kelola SPBU
|
||||||
|
</button>
|
||||||
|
<button class="btn-mode" id="mode-jalan" onclick="changeMode('jalan')">
|
||||||
|
<i class="bi bi-slash-lg text-danger"></i> Kelola Jalan
|
||||||
|
</button>
|
||||||
|
<button class="btn-mode" id="mode-parsil" onclick="changeMode('parsil')">
|
||||||
|
<i class="bi bi-square-fill text-primary"></i> Kelola Tanah
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="map"></div>
|
||||||
|
|
||||||
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
|
||||||
|
<script src="https://cdn.rawgit.com/makinacorpus/Leaflet.GeometryUtil/master/dist/leaflet.geometryutil.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var currentMode = 'view';
|
||||||
|
var map = L.map('map', { zoomControl: false }).setView([-0.0263, 109.3425], 13);
|
||||||
|
L.control.zoom({ position: 'bottomright' }).addTo(map);
|
||||||
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
|
||||||
|
|
||||||
|
var drawnItems = new L.FeatureGroup().addTo(map);
|
||||||
|
var drawJalan = new L.Draw.Polyline(map, { shapeOptions: { color: '#dc3545', weight: 5 } });
|
||||||
|
var drawParsil = new L.Draw.Polygon(map, { shapeOptions: { color: '#198754', fillOpacity: 0.4 } });
|
||||||
|
|
||||||
|
function toggleSidebar() {
|
||||||
|
document.getElementById('sidebar-menu').classList.toggle('collapsed');
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeMode(mode) {
|
||||||
|
currentMode = mode;
|
||||||
|
document.querySelectorAll('.btn-mode').forEach(b => b.classList.remove('active'));
|
||||||
|
document.getElementById('mode-' + mode).classList.add('active');
|
||||||
|
|
||||||
|
drawJalan.disable();
|
||||||
|
drawParsil.disable();
|
||||||
|
|
||||||
|
if(mode === 'jalan') drawJalan.enable();
|
||||||
|
if(mode === 'parsil') drawParsil.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- READ DATA ---
|
||||||
|
const colorJ = (s) => s=='Nasional'?'#ff0000':s=='Provinsi'?'#ffae00':'#0000ff';
|
||||||
|
const colorP = (s) => s=='SHM'?'#27ae60':s=='HGB'?'#f1c40f':s=='HGU'?'#e67e22':'#9b59b6';
|
||||||
|
|
||||||
|
// Load Jalan
|
||||||
|
<?php
|
||||||
|
$qJ = mysqli_query($conn, "SELECT * FROM jalan");
|
||||||
|
while($j = mysqli_fetch_assoc($qJ)) { ?>
|
||||||
|
L.geoJSON(<?= $j['geojson'] ?>, {
|
||||||
|
style: { color: colorJ('<?= $j['status_jalan'] ?>'), weight: 5 }
|
||||||
|
}).bindPopup(`
|
||||||
|
<div class="popup-header">DATA JALAN</div>
|
||||||
|
<div class="popup-body">
|
||||||
|
<h6 class="fw-bold mb-1"><?= $j['nama_jalan'] ?></h6>
|
||||||
|
<span class="badge bg-primary mb-2"><?= $j['status_jalan'] ?></span>
|
||||||
|
<p class="small mb-2">Panjang: <?= $j['panjang_m'] ?> m</p>
|
||||||
|
<a href="hapus_jalan.php?id=<?= $j['id'] ?>" class="btn btn-danger btn-sm w-100" onclick="return confirm('Hapus?')">HAPUS</a>
|
||||||
|
</div>
|
||||||
|
`).addTo(map);
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
// Load Parsil
|
||||||
|
<?php
|
||||||
|
$qP = mysqli_query($conn, "SELECT * FROM parsil");
|
||||||
|
while($p = mysqli_fetch_assoc($qP)) { ?>
|
||||||
|
L.geoJSON(<?= $p['geojson'] ?>, {
|
||||||
|
style: { color: colorP('<?= $p['status_milik'] ?>'), fillOpacity: 0.5 }
|
||||||
|
}).bindPopup(`
|
||||||
|
<div class="popup-header">DATA TANAH</div>
|
||||||
|
<div class="popup-body">
|
||||||
|
<h6 class="fw-bold mb-1">ID: <?= $p['id_parsil'] ?></h6>
|
||||||
|
<span class="badge bg-success mb-2"><?= $p['status_milik'] ?></span>
|
||||||
|
<p class="small mb-2">Luas: <?= $p['luas_m2'] ?> m²</p>
|
||||||
|
<a href="hapus_parsil.php?id=<?= $p['id'] ?>" class="btn btn-danger btn-sm w-100" onclick="return confirm('Hapus?')">HAPUS</a>
|
||||||
|
</div>
|
||||||
|
`).addTo(map);
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
// --- LAYER GROUPS SPBU ---
|
||||||
|
var spbu24Jam = L.layerGroup().addTo(map);
|
||||||
|
var spbuTidak24Jam = L.layerGroup().addTo(map);
|
||||||
|
|
||||||
|
var overlays = {
|
||||||
|
"<span style='color:#2ecc71; font-weight:bold;'><i class='bi bi-geo-alt-fill'></i> SPBU Penuh (24 Jam)</span>": spbu24Jam,
|
||||||
|
"<span style='color:#ff6b6b; font-weight:bold;'><i class='bi bi-geo-alt-fill'></i> SPBU Terbatas</span>": spbuTidak24Jam
|
||||||
|
};
|
||||||
|
L.control.layers(null, overlays, {collapsed: false, position: 'topright'}).addTo(map);
|
||||||
|
|
||||||
|
// Load SPBU dengan Fitur Drag (Geser) Aktif
|
||||||
|
<?php
|
||||||
|
$qS = mysqli_query($conn, "SELECT * FROM spbu");
|
||||||
|
while($s = mysqli_fetch_assoc($qS)) { ?>
|
||||||
|
var marker = L.marker([<?= $s['latitude'] ?>, <?= $s['longitude'] ?>], {
|
||||||
|
// Aktifkan draggable agar bisa digeser
|
||||||
|
draggable: true,
|
||||||
|
icon: L.icon({
|
||||||
|
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-<?= $s['is_24jam']=='Ya'?'green':'red' ?>.png',
|
||||||
|
iconSize: [25, 41],
|
||||||
|
iconAnchor: [12, 41],
|
||||||
|
popupAnchor: [1, -34]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
// Masukkan marker ke dalam layer group yang sesuai
|
||||||
|
<?php if ($s['is_24jam'] == 'Ya') { ?>
|
||||||
|
marker.addTo(spbu24Jam);
|
||||||
|
<?php } else { ?>
|
||||||
|
marker.addTo(spbuTidak24Jam);
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
// Konten Popup (Tetap sama)
|
||||||
|
marker.bindPopup(`
|
||||||
|
<div class="popup-header">INFORMASI SPBU</div>
|
||||||
|
<div class="popup-body">
|
||||||
|
<h6 class="fw-bold mb-1"><?= strtoupper($s['nama_spbu']) ?></h6>
|
||||||
|
<span class="badge <?= $s['is_24jam']=='Ya'?'bg-success':'bg-danger' ?> w-100 mb-3 py-2"><?= $s['is_24jam']=='Ya'?'24 JAM':'TERBATAS' ?></span>
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<a href="https://wa.me/<?= preg_replace('/[^0-9]/','',$s['no_wa']) ?>" target="_blank" class="btn-wa">WHATSAPP</a>
|
||||||
|
<a href="hapus_spbu.php?id=<?= $s['id'] ?>" class="btn-delete-icon" onclick="return confirm('Hapus?')"><i class="bi bi-trash3-fill"></i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
// --- FUNGSI UPDATE POSISI SAAT DIGESER ---
|
||||||
|
marker.on('dragend', function(e) {
|
||||||
|
var latlng = e.target.getLatLng();
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append('id', "<?= $s['id'] ?>");
|
||||||
|
formData.append('lat', latlng.lat);
|
||||||
|
formData.append('lng', latlng.lng);
|
||||||
|
|
||||||
|
// Kirim perubahan ke file update_posisi.php
|
||||||
|
fetch('update_posisi.php', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(data => {
|
||||||
|
console.log('Posisi diperbarui:', latlng.lat, latlng.lng);
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Gagal update:', error));
|
||||||
|
});
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
// --- CREATE DATA ---
|
||||||
|
|
||||||
|
map.on('click', function(e) {
|
||||||
|
if(currentMode !== 'spbu') return;
|
||||||
|
var content = `
|
||||||
|
<div class="popup-header">TAMBAH SPBU</div>
|
||||||
|
<div class="popup-body">
|
||||||
|
<form action="simpan_spbu.php" method="POST">
|
||||||
|
<label class="form-label">Nama SPBU</label><input type="text" name="nama" class="form-control-modern" required>
|
||||||
|
<label class="form-label">WhatsApp</label><input type="text" name="wa" class="form-control-modern" required>
|
||||||
|
<label class="form-label">Status</label>
|
||||||
|
<div class="status-toggle">
|
||||||
|
<input type="radio" name="aktif" id="ya" value="Ya" checked><label for="ya" class="lab-ya">24 Jam</label>
|
||||||
|
<input type="radio" name="aktif" id="tidak" value="Tidak"><label for="tidak" class="lab-tidak">Terbatas</label>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="lat" value="${e.latlng.lat}"><input type="hidden" name="lng" value="${e.latlng.lng}">
|
||||||
|
<button type="submit" class="btn btn-primary w-100 py-2 fw-bold" style="border-radius:10px;">SIMPAN</button>
|
||||||
|
</form>
|
||||||
|
</div>`;
|
||||||
|
L.popup().setLatLng(e.latlng).setContent(content).openOn(map);
|
||||||
|
});
|
||||||
|
|
||||||
|
map.on(L.Draw.Event.CREATED, function (e) {
|
||||||
|
var layer = e.layer;
|
||||||
|
var type = e.layerType;
|
||||||
|
var geojson = JSON.stringify(layer.toGeoJSON().geometry);
|
||||||
|
drawnItems.addLayer(layer);
|
||||||
|
|
||||||
|
if (type === 'polyline') {
|
||||||
|
var d = 0, ll = layer.getLatLngs();
|
||||||
|
for (var i=0; i<ll.length-1; i++) d += ll[i].distanceTo(ll[i+1]);
|
||||||
|
var popup = `
|
||||||
|
<div class="popup-header">SIMPAN JALAN BARU</div>
|
||||||
|
<div class="popup-body">
|
||||||
|
<form action="simpan_jalan.php" method="POST">
|
||||||
|
<label class="form-label">Nama Jalan</label><input type="text" name="nama_jalan" class="form-control-modern" required>
|
||||||
|
<label class="form-label">Status</label>
|
||||||
|
<select name="status" class="form-control-modern"><option value="Nasional">Nasional</option><option value="Provinsi">Provinsi</option><option value="Kabupaten">Kabupaten</option></select>
|
||||||
|
<label class="form-label">Panjang (m)</label><input type="text" name="panjang" value="${d.toFixed(2)}" class="form-control-modern bg-light" readonly>
|
||||||
|
<input type="hidden" name="geojson" value='${geojson}'>
|
||||||
|
<button type="submit" class="btn btn-primary w-100 py-2 fw-bold">SIMPAN JALAN</button>
|
||||||
|
</form>
|
||||||
|
</div>`;
|
||||||
|
layer.bindPopup(popup, {minWidth: 300}).openPopup();
|
||||||
|
} else if (type === 'polygon') {
|
||||||
|
var area = L.GeometryUtil.geodesicArea(layer.getLatLngs()[0]);
|
||||||
|
var popup = `
|
||||||
|
<div class="popup-header">SIMPAN TANAH BARU</div>
|
||||||
|
<div class="popup-body">
|
||||||
|
<form action="simpan_parsil.php" method="POST">
|
||||||
|
<label class="form-label">ID Parsil</label><input type="text" name="id_parsil" class="form-control-modern" required>
|
||||||
|
<label class="form-label">Jenis Hak</label>
|
||||||
|
<select name="status" class="form-control-modern"><option value="SHM">SHM</option><option value="HGB">HGB</option><option value="HGU">HGU</option><option value="HP">HP</option></select>
|
||||||
|
<label class="form-label">Luas (m²)</label><input type="text" name="luas" value="${area.toFixed(2)}" class="form-control-modern bg-light" readonly>
|
||||||
|
<input type="hidden" name="geojson" value='${geojson}'>
|
||||||
|
<button type="submit" class="btn btn-success w-100 py-2 fw-bold">SIMPAN TANAH</button>
|
||||||
|
</form>
|
||||||
|
</div>`;
|
||||||
|
layer.bindPopup(popup, {minWidth: 300}).openPopup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
$host = "127.0.0.1";
|
||||||
|
$user = "root";
|
||||||
|
$pass = "";
|
||||||
|
$db = "webgis";
|
||||||
|
$conn = mysqli_connect($host, $user, $pass, $db);
|
||||||
+538
@@ -0,0 +1,538 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="id">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Analisis Radius Bantuan — WebGIS</title>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" rel="stylesheet">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;700;800&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body { font-family: 'Plus Jakarta Sans', sans-serif; background: #0b132b; color: #fff; overflow: hidden; }
|
||||||
|
#map { height: 100vh; width: 100%; z-index: 1; }
|
||||||
|
|
||||||
|
/* SIDEBAR */
|
||||||
|
.sidebar {
|
||||||
|
position: absolute; top: 20px; left: 20px; z-index: 1000;
|
||||||
|
width: 270px; background: rgba(11,19,43,0.88);
|
||||||
|
backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
|
||||||
|
border: 1px solid rgba(255,255,255,0.1); border-radius: 20px;
|
||||||
|
padding: 22px; box-shadow: 0 8px 40px rgba(0,0,0,0.6);
|
||||||
|
}
|
||||||
|
.sidebar-brand {
|
||||||
|
display: flex; align-items: center; gap: 10px;
|
||||||
|
font-size: 0.7rem; font-weight: 800; color: #5bc0be;
|
||||||
|
text-transform: uppercase; letter-spacing: 2px;
|
||||||
|
cursor: pointer; user-select: none;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.sidebar-brand span { display: flex; align-items: center; gap: 10px; }
|
||||||
|
.sidebar-brand .icon-wrap {
|
||||||
|
width: 34px; height: 34px; border-radius: 10px;
|
||||||
|
background: linear-gradient(135deg, #5bc0be, #3a506b);
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
#chevron { transition: transform .3s; }
|
||||||
|
.sidebar.open #chevron { transform: rotate(180deg); }
|
||||||
|
.sidebar-body { overflow: hidden; max-height: 0; transition: max-height .4s ease; }
|
||||||
|
.sidebar.open .sidebar-body { max-height: 600px; }
|
||||||
|
|
||||||
|
.divider { border: none; border-top: 1px solid rgba(255,255,255,0.08); margin: 16px 0; }
|
||||||
|
|
||||||
|
.section-label {
|
||||||
|
font-size: 0.62rem; font-weight: 800; color: #5bc0be;
|
||||||
|
text-transform: uppercase; letter-spacing: 1.5px; margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.btn-mode {
|
||||||
|
width: 100%; display: flex; align-items: center; gap: 12px;
|
||||||
|
padding: 12px 14px; border-radius: 12px; border: 1px solid rgba(255,255,255,0.05);
|
||||||
|
background: rgba(255,255,255,0.03); color: #e0e1dd; font-size: 0.85rem;
|
||||||
|
font-weight: 600; cursor: pointer; transition: all .25s; margin-bottom: 8px;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
.btn-mode i { font-size: 1.1rem; }
|
||||||
|
.btn-mode:hover { background: rgba(58,80,107,0.4); transform: translateX(4px); border-color: rgba(91,192,190,0.3); }
|
||||||
|
.btn-mode.active {
|
||||||
|
background: linear-gradient(135deg,#1c2541,#3a506b);
|
||||||
|
border-color: #5bc0be; color: #fff;
|
||||||
|
box-shadow: 0 4px 15px rgba(91,192,190,0.35);
|
||||||
|
}
|
||||||
|
.btn-mode.active i { color: #6fffe9; }
|
||||||
|
|
||||||
|
/* LEGEND */
|
||||||
|
.legend { margin-top: 4px; }
|
||||||
|
.legend-item { display: flex; align-items: center; gap: 9px; font-size: 0.78rem; color: #b0bec5; margin-bottom: 7px; }
|
||||||
|
.legend-dot { width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0; }
|
||||||
|
.legend-dot.ibadah { background: #6fffe9; box-shadow: 0 0 6px #6fffe9; }
|
||||||
|
.legend-dot.hijau { background: #2ecc71; box-shadow: 0 0 6px #2ecc71; }
|
||||||
|
.legend-dot.merah { background: #e74c3c; box-shadow: 0 0 6px #e74c3c; }
|
||||||
|
.legend-circle { width: 14px; height: 14px; border-radius: 50%; border: 2px solid #5bc0be; flex-shrink: 0; }
|
||||||
|
|
||||||
|
/* STATS BAR */
|
||||||
|
.stats-bar {
|
||||||
|
position: absolute; bottom: 24px; left: 50%; transform: translateX(-50%);
|
||||||
|
z-index: 1000; display: flex; gap: 12px;
|
||||||
|
}
|
||||||
|
.stat-card {
|
||||||
|
background: rgba(11,19,43,0.88); backdrop-filter: blur(14px);
|
||||||
|
border: 1px solid rgba(255,255,255,0.1); border-radius: 14px;
|
||||||
|
padding: 10px 20px; text-align: center; min-width: 120px;
|
||||||
|
}
|
||||||
|
.stat-card .val { font-size: 1.6rem; font-weight: 800; line-height: 1; }
|
||||||
|
.stat-card .lbl { font-size: 0.65rem; text-transform: uppercase; letter-spacing: 1px; color: #78909c; margin-top: 3px; }
|
||||||
|
.stat-ibadah .val { color: #6fffe9; }
|
||||||
|
.stat-hijau .val { color: #2ecc71; }
|
||||||
|
.stat-merah .val { color: #e74c3c; }
|
||||||
|
|
||||||
|
/* TOAST */
|
||||||
|
#toast {
|
||||||
|
position: absolute; top: 24px; right: 24px; z-index: 9999;
|
||||||
|
background: rgba(11,19,43,0.95); border: 1px solid rgba(255,255,255,0.12);
|
||||||
|
border-radius: 14px; padding: 14px 20px; font-size: 0.82rem;
|
||||||
|
max-width: 280px; display: none; backdrop-filter: blur(12px);
|
||||||
|
box-shadow: 0 8px 30px rgba(0,0,0,0.5); animation: slideIn .3s ease;
|
||||||
|
}
|
||||||
|
@keyframes slideIn { from { opacity:0; transform:translateX(30px); } to { opacity:1; transform:translateX(0); } }
|
||||||
|
|
||||||
|
/* POPUP */
|
||||||
|
.leaflet-popup-content-wrapper {
|
||||||
|
background: rgba(14,23,44,0.96) !important; backdrop-filter: blur(16px);
|
||||||
|
border: 1px solid rgba(255,255,255,0.1); border-radius: 18px !important;
|
||||||
|
padding: 0 !important; overflow: hidden;
|
||||||
|
box-shadow: 0 15px 40px rgba(0,0,0,0.6) !important; color: #fff;
|
||||||
|
}
|
||||||
|
.leaflet-popup-tip { background: rgba(14,23,44,0.96) !important; }
|
||||||
|
.leaflet-popup-content { margin: 0 !important; width: 300px !important; }
|
||||||
|
.leaflet-popup-close-button { color: #fff !important; top: 12px !important; right: 12px !important; font-size: 18px !important; z-index: 10; }
|
||||||
|
.leaflet-popup-close-button:hover { color: #5bc0be !important; }
|
||||||
|
|
||||||
|
.p-header {
|
||||||
|
background: linear-gradient(135deg,#0b132b,#1c2541);
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.08);
|
||||||
|
padding: 16px 40px 16px 18px;
|
||||||
|
font-size: 0.75rem; font-weight: 800;
|
||||||
|
text-transform: uppercase; letter-spacing: 1.5px; color: #6fffe9;
|
||||||
|
}
|
||||||
|
.p-body { padding: 18px; }
|
||||||
|
.f-label {
|
||||||
|
font-size: 0.65rem; font-weight: 700; color: #5bc0be;
|
||||||
|
text-transform: uppercase; letter-spacing: 1px;
|
||||||
|
display: block; margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.f-input {
|
||||||
|
width: 100%; background: rgba(0,0,0,0.25); border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
border-radius: 10px; padding: 11px 13px; font-size: 0.84rem; color: #fff;
|
||||||
|
font-family: inherit; transition: border-color .25s; margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
.f-input:focus { outline: none; border-color: #5bc0be; background: rgba(0,0,0,0.4); }
|
||||||
|
.f-input::placeholder { color: rgba(255,255,255,0.35); }
|
||||||
|
.btn-submit {
|
||||||
|
width: 100%; padding: 12px; border-radius: 11px; border: none; cursor: pointer;
|
||||||
|
font-weight: 700; font-size: 0.88rem; font-family: inherit;
|
||||||
|
background: linear-gradient(135deg,#5bc0be,#3a506b); color: #fff;
|
||||||
|
transition: transform .25s, box-shadow .25s;
|
||||||
|
}
|
||||||
|
.btn-submit:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(91,192,190,0.4); }
|
||||||
|
.info-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; font-size: 0.82rem; }
|
||||||
|
.info-row .key { color: #78909c; }
|
||||||
|
.info-row .val { font-weight: 700; }
|
||||||
|
.badge-warna {
|
||||||
|
display: inline-block; padding: 4px 12px; border-radius: 20px;
|
||||||
|
font-size: 0.7rem; font-weight: 700; text-transform: uppercase;
|
||||||
|
}
|
||||||
|
.badge-hijau { background: rgba(46,204,113,0.2); color: #2ecc71; border: 1px solid #2ecc71; }
|
||||||
|
.badge-merah { background: rgba(231,76,60,0.2); color: #e74c3c; border: 1px solid #e74c3c; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- SIDEBAR -->
|
||||||
|
<div class="sidebar" id="sidebar">
|
||||||
|
<div class="sidebar-brand" onclick="toggleSidebar()">
|
||||||
|
<span><div class="icon-wrap"><i class="bi bi-broadcast-pin"></i></div> Radius Bantuan</span>
|
||||||
|
<i class="bi bi-chevron-down" id="chevron"></i>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-body">
|
||||||
|
<hr class="divider">
|
||||||
|
<p class="section-label">Mode Input</p>
|
||||||
|
<button class="btn-mode active" id="btn-view" onclick="setMode('view')">
|
||||||
|
<i class="bi bi-eye-fill"></i> Mode Lihat
|
||||||
|
</button>
|
||||||
|
<button class="btn-mode" id="btn-ibadah" onclick="setMode('ibadah')">
|
||||||
|
<i class="bi bi-building-fill" style="color:#6fffe9"></i> Tambah Rumah Ibadah
|
||||||
|
</button>
|
||||||
|
<button class="btn-mode" id="btn-penduduk" onclick="setMode('penduduk')">
|
||||||
|
<i class="bi bi-person-fill" style="color:#f39c12"></i> Tambah Penduduk
|
||||||
|
</button>
|
||||||
|
<hr class="divider">
|
||||||
|
<p class="section-label">Legenda</p>
|
||||||
|
<div class="legend">
|
||||||
|
<div class="legend-item"><div class="legend-dot ibadah"></div> Rumah Ibadah</div>
|
||||||
|
<div class="legend-item"><div class="legend-circle"></div> Radius Jangkauan</div>
|
||||||
|
<div class="legend-item"><div class="legend-dot hijau"></div> Penduduk — Tercakup</div>
|
||||||
|
<div class="legend-item"><div class="legend-dot merah"></div> Penduduk — Belum Tercakup</div>
|
||||||
|
</div>
|
||||||
|
<hr class="divider">
|
||||||
|
<button class="btn-mode" onclick="loadData()" style="color:#6fffe9;">
|
||||||
|
<i class="bi bi-arrow-clockwise"></i> Refresh Data
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- STATS BAR -->
|
||||||
|
<div class="stats-bar">
|
||||||
|
<div class="stat-card stat-ibadah">
|
||||||
|
<div class="val" id="stat-ibadah">0</div>
|
||||||
|
<div class="lbl">Rumah Ibadah</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card stat-hijau">
|
||||||
|
<div class="val" id="stat-hijau">0</div>
|
||||||
|
<div class="lbl">Tercakup</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card stat-merah">
|
||||||
|
<div class="val" id="stat-merah">0</div>
|
||||||
|
<div class="lbl">Belum Tercakup</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- TOAST -->
|
||||||
|
<div id="toast"></div>
|
||||||
|
|
||||||
|
<div id="map"></div>
|
||||||
|
|
||||||
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@turf/turf@6/turf.min.js"></script>
|
||||||
|
<script>
|
||||||
|
// ===== INIT MAP =====
|
||||||
|
const map = L.map('map', { zoomControl: false }).setView([-0.0263, 109.3425], 13);
|
||||||
|
L.control.zoom({ position: 'bottomright' }).addTo(map);
|
||||||
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
|
attribution: '© OpenStreetMap contributors'
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
// ===== STATE =====
|
||||||
|
let currentMode = 'view';
|
||||||
|
let layerIbadah = L.layerGroup().addTo(map);
|
||||||
|
let layerPenduduk = L.layerGroup().addTo(map);
|
||||||
|
let allIbadah = [];
|
||||||
|
let allPenduduk = [];
|
||||||
|
|
||||||
|
// ===== ICON FACTORY =====
|
||||||
|
function makeIcon(color) {
|
||||||
|
const colors = {
|
||||||
|
cyan: '#6fffe9',
|
||||||
|
green: '#2ecc71',
|
||||||
|
red: '#e74c3c',
|
||||||
|
orange: '#f39c12'
|
||||||
|
};
|
||||||
|
const hex = colors[color] || colors.cyan;
|
||||||
|
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 42" width="32" height="42">
|
||||||
|
<filter id="sh"><feDropShadow dx="0" dy="2" stdDeviation="2" flood-color="${hex}" flood-opacity="0.6"/></filter>
|
||||||
|
<path filter="url(#sh)" d="M16 0C9.4 0 4 5.4 4 12c0 9 12 26 12 26S28 21 28 12C28 5.4 22.6 0 16 0z" fill="${hex}"/>
|
||||||
|
<circle cx="16" cy="12" r="5" fill="#0b132b"/>
|
||||||
|
</svg>`;
|
||||||
|
return L.divIcon({
|
||||||
|
html: svg, className: '', iconSize: [32,42], iconAnchor: [16,42], popupAnchor: [0,-40]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== SIDEBAR =====
|
||||||
|
function toggleSidebar() {
|
||||||
|
document.getElementById('sidebar').classList.toggle('open');
|
||||||
|
}
|
||||||
|
// Buka sidebar saat load
|
||||||
|
document.getElementById('sidebar').classList.add('open');
|
||||||
|
|
||||||
|
// ===== MODE =====
|
||||||
|
function setMode(mode) {
|
||||||
|
currentMode = mode;
|
||||||
|
document.querySelectorAll('.btn-mode').forEach(b => b.classList.remove('active'));
|
||||||
|
document.getElementById('btn-' + mode).classList.add('active');
|
||||||
|
showToast(
|
||||||
|
mode === 'view' ? '<i class="bi bi-eye-fill"></i> Mode Lihat aktif' :
|
||||||
|
mode === 'ibadah' ? '<i class="bi bi-building-fill"></i> Klik peta untuk tambah Rumah Ibadah' :
|
||||||
|
'<i class="bi bi-person-fill"></i> Klik peta untuk tambah Penduduk',
|
||||||
|
mode === 'view' ? '#5bc0be' : '#f39c12'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== TOAST =====
|
||||||
|
let toastTimer;
|
||||||
|
function showToast(msg, color = '#5bc0be') {
|
||||||
|
const el = document.getElementById('toast');
|
||||||
|
el.innerHTML = msg;
|
||||||
|
el.style.borderLeftColor = color;
|
||||||
|
el.style.borderLeft = `3px solid ${color}`;
|
||||||
|
el.style.display = 'block';
|
||||||
|
clearTimeout(toastTimer);
|
||||||
|
toastTimer = setTimeout(() => { el.style.display = 'none'; }, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== STATS =====
|
||||||
|
function updateStats() {
|
||||||
|
document.getElementById('stat-ibadah').textContent = allIbadah.length;
|
||||||
|
const hijau = allPenduduk.filter(p => p.status_warna === 'Hijau').length;
|
||||||
|
const merah = allPenduduk.filter(p => p.status_warna === 'Merah').length;
|
||||||
|
document.getElementById('stat-hijau').textContent = hijau;
|
||||||
|
document.getElementById('stat-merah').textContent = merah;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== LOAD DATA =====
|
||||||
|
async function loadData() {
|
||||||
|
try {
|
||||||
|
const res = await fetch('ambil.php');
|
||||||
|
const data = await res.json();
|
||||||
|
allIbadah = data.ibadah || [];
|
||||||
|
allPenduduk = data.penduduk || [];
|
||||||
|
|
||||||
|
renderIbadah();
|
||||||
|
renderPenduduk();
|
||||||
|
await cekRadius();
|
||||||
|
updateStats();
|
||||||
|
} catch(e) {
|
||||||
|
showToast('<i class="bi bi-exclamation-triangle"></i> Gagal memuat data.', '#e74c3c');
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== RENDER IBADAH =====
|
||||||
|
function renderIbadah() {
|
||||||
|
layerIbadah.clearLayers();
|
||||||
|
allIbadah.forEach(d => {
|
||||||
|
const marker = L.marker([d.latitude, d.longitude], { icon: makeIcon('cyan') });
|
||||||
|
const circle = L.circle([d.latitude, d.longitude], {
|
||||||
|
radius: d.radius,
|
||||||
|
color: '#5bc0be', fillColor: '#5bc0be',
|
||||||
|
fillOpacity: 0.08, weight: 1.5, dashArray: '6,4'
|
||||||
|
});
|
||||||
|
|
||||||
|
marker.bindPopup(`
|
||||||
|
<div class="p-header"><i class="bi bi-building-fill"></i> Rumah Ibadah</div>
|
||||||
|
<div class="p-body">
|
||||||
|
<div class="info-row"><span class="key">Nama</span><span class="val">${d.nama_ibadah}</span></div>
|
||||||
|
<div class="info-row"><span class="key">Radius</span><span class="val">${d.radius.toLocaleString()} m</span></div>
|
||||||
|
<div class="info-row"><span class="key">Koordinat</span><span class="val">${d.latitude.toFixed(5)}, ${d.longitude.toFixed(5)}</span></div>
|
||||||
|
<button class="btn-submit" style="background: rgba(231,76,60,0.1); color: #e74c3c; border: 1px solid #e74c3c; margin-top: 12px; box-shadow: none;" onclick="hapusData('ibadah', ${d.id})"><i class="bi bi-trash"></i> Hapus Rumah Ibadah</button>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
layerIbadah.addLayer(circle);
|
||||||
|
layerIbadah.addLayer(marker);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== RENDER PENDUDUK =====
|
||||||
|
function renderPenduduk() {
|
||||||
|
layerPenduduk.clearLayers();
|
||||||
|
allPenduduk.forEach(d => {
|
||||||
|
const color = d.status_warna === 'Hijau' ? 'green' : 'red';
|
||||||
|
const marker = L.marker([d.latitude, d.longitude], { icon: makeIcon(color) });
|
||||||
|
const badge = d.status_warna === 'Hijau'
|
||||||
|
? '<span class="badge-warna badge-hijau">Tercakup</span>'
|
||||||
|
: '<span class="badge-warna badge-merah">Belum Tercakup</span>';
|
||||||
|
|
||||||
|
marker.bindPopup(`
|
||||||
|
<div class="p-header"><i class="bi bi-person-fill"></i> Data Penduduk</div>
|
||||||
|
<div class="p-body">
|
||||||
|
<div class="info-row"><span class="key">Nama</span><span class="val">${d.nama_penduduk}</span></div>
|
||||||
|
<div class="info-row"><span class="key">Status</span>${badge}</div>
|
||||||
|
<div class="info-row"><span class="key">Koordinat</span><span class="val">${d.latitude.toFixed(5)}, ${d.longitude.toFixed(5)}</span></div>
|
||||||
|
<button class="btn-submit" style="background: rgba(231,76,60,0.1); color: #e74c3c; border: 1px solid #e74c3c; margin-top: 12px; box-shadow: none;" onclick="hapusData('penduduk', ${d.id})"><i class="bi bi-trash"></i> Hapus Data Penduduk</button>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
layerPenduduk.addLayer(marker);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== CEK RADIUS (Proximity Analysis dengan Turf.js) =====
|
||||||
|
async function cekRadius() {
|
||||||
|
if (allIbadah.length === 0 || allPenduduk.length === 0) return;
|
||||||
|
|
||||||
|
for (const penduduk of allPenduduk) {
|
||||||
|
const ptPenduduk = turf.point([penduduk.longitude, penduduk.latitude]);
|
||||||
|
let dalamRadius = false;
|
||||||
|
|
||||||
|
for (const ibadah of allIbadah) {
|
||||||
|
const ptIbadah = turf.point([ibadah.longitude, ibadah.latitude]);
|
||||||
|
// turf.distance mengembalikan km, ubah ke meter
|
||||||
|
const jarakM = turf.distance(ptPenduduk, ptIbadah, { units: 'kilometers' }) * 1000;
|
||||||
|
if (jarakM <= ibadah.radius) {
|
||||||
|
dalamRadius = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const warnaTarget = dalamRadius ? 'Hijau' : 'Merah';
|
||||||
|
|
||||||
|
// Hanya kirim update jika status berubah (efisiensi request)
|
||||||
|
if (penduduk.status_warna !== warnaTarget) {
|
||||||
|
penduduk.status_warna = warnaTarget; // update lokal
|
||||||
|
await updateWarnaPenduduk(penduduk.id, warnaTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
renderPenduduk();
|
||||||
|
updateStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== UPDATE STATUS WARNA KE SERVER =====
|
||||||
|
async function updateWarnaPenduduk(id, warna) {
|
||||||
|
try {
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('action', 'update_warna');
|
||||||
|
fd.append('id', id);
|
||||||
|
fd.append('status_warna', warna);
|
||||||
|
const res = await fetch('ambil.php', { method: 'POST', body: fd });
|
||||||
|
const data = await res.json();
|
||||||
|
console.log('Update warna:', data.message);
|
||||||
|
} catch(e) {
|
||||||
|
console.error('Gagal update warna:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== HAPUS DATA =====
|
||||||
|
async function hapusData(type, id) {
|
||||||
|
if(!confirm("Yakin ingin menghapus data ini?")) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('action', 'hapus');
|
||||||
|
fd.append('type_hapus', type);
|
||||||
|
fd.append('id', id);
|
||||||
|
|
||||||
|
const res = await fetch('ambil.php', { method: 'POST', body: fd });
|
||||||
|
const text = await res.text();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(text);
|
||||||
|
if (data.status === 'success') {
|
||||||
|
map.closePopup();
|
||||||
|
showToast(`<i class="bi bi-trash"></i> ${data.message}`, '#e74c3c');
|
||||||
|
await loadData();
|
||||||
|
} else {
|
||||||
|
showToast(`<i class="bi bi-x-circle"></i> ${data.message}`, '#e74c3c');
|
||||||
|
}
|
||||||
|
} catch(parseErr) {
|
||||||
|
showToast(`<i class="bi bi-bug"></i> Error Server: ${text.substring(0, 50)}...`, '#e74c3c');
|
||||||
|
console.error('Response bukan JSON:', text);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
showToast('<i class="bi bi-wifi-off"></i> Gagal menghapus data.', '#e74c3c');
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== KLIK PETA =====
|
||||||
|
map.on('click', function(e) {
|
||||||
|
if (currentMode === 'view') return;
|
||||||
|
|
||||||
|
const { lat, lng } = e.latlng;
|
||||||
|
|
||||||
|
if (currentMode === 'ibadah') {
|
||||||
|
const content = `
|
||||||
|
<div class="p-header"><i class="bi bi-building-fill"></i> Tambah Rumah Ibadah</div>
|
||||||
|
<div class="p-body">
|
||||||
|
<label class="f-label">Nama Rumah Ibadah</label>
|
||||||
|
<input id="inp-nama-ibadah" class="f-input" type="text" placeholder="cth: Masjid Al-Hikmah" required>
|
||||||
|
<label class="f-label">Radius Jangkauan (meter)</label>
|
||||||
|
<input id="inp-radius" class="f-input" type="number" placeholder="cth: 500" value="500" min="50" max="50000" required>
|
||||||
|
<button class="btn-submit" onclick="simpanIbadah(${lat}, ${lng})">
|
||||||
|
<i class="bi bi-check2-circle"></i> Simpan Rumah Ibadah
|
||||||
|
</button>
|
||||||
|
</div>`;
|
||||||
|
L.popup({ minWidth: 300 }).setLatLng(e.latlng).setContent(content).openOn(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMode === 'penduduk') {
|
||||||
|
const content = `
|
||||||
|
<div class="p-header"><i class="bi bi-person-fill"></i> Tambah Data Penduduk</div>
|
||||||
|
<div class="p-body">
|
||||||
|
<label class="f-label">Nama Penduduk</label>
|
||||||
|
<input id="inp-nama-penduduk" class="f-input" type="text" placeholder="cth: Budi Santoso" required>
|
||||||
|
<button class="btn-submit" onclick="simpanPenduduk(${lat}, ${lng})">
|
||||||
|
<i class="bi bi-check2-circle"></i> Simpan Penduduk
|
||||||
|
</button>
|
||||||
|
</div>`;
|
||||||
|
L.popup({ minWidth: 300 }).setLatLng(e.latlng).setContent(content).openOn(map);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ===== SIMPAN IBADAH =====
|
||||||
|
async function simpanIbadah(lat, lng) {
|
||||||
|
const nama = document.getElementById('inp-nama-ibadah')?.value?.trim();
|
||||||
|
const radius = parseInt(document.getElementById('inp-radius')?.value);
|
||||||
|
|
||||||
|
if (!nama) { showToast('<i class="bi bi-exclamation"></i> Nama tidak boleh kosong!', '#e74c3c'); return; }
|
||||||
|
if (!radius || radius < 1) { showToast('<i class="bi bi-exclamation"></i> Radius tidak valid!', '#e74c3c'); return; }
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('type', 'ibadah');
|
||||||
|
fd.append('nama_ibadah', nama);
|
||||||
|
fd.append('latitude', lat);
|
||||||
|
fd.append('longitude', lng);
|
||||||
|
fd.append('radius', radius);
|
||||||
|
|
||||||
|
const res = await fetch('ambil.php', { method: 'POST', body: fd });
|
||||||
|
const text = await res.text(); // Ambil teks mentah dulu untuk debug
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(text);
|
||||||
|
if (data.status === 'success') {
|
||||||
|
map.closePopup();
|
||||||
|
showToast(`<i class="bi bi-check-circle-fill"></i> ${data.message}`, '#2ecc71');
|
||||||
|
await loadData();
|
||||||
|
} else {
|
||||||
|
showToast(`<i class="bi bi-x-circle"></i> ${data.message}`, '#e74c3c');
|
||||||
|
}
|
||||||
|
} catch(parseErr) {
|
||||||
|
// Jika bukan JSON, tampilkan 50 karakter pertama dari teks error-nya
|
||||||
|
showToast(`<i class="bi bi-bug"></i> Error Server: ${text.substring(0, 50)}...`, '#e74c3c');
|
||||||
|
console.error('Response bukan JSON:', text);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
showToast('<i class="bi bi-wifi-off"></i> Gagal terhubung ke ambil.php', '#e74c3c');
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== SIMPAN PENDUDUK =====
|
||||||
|
async function simpanPenduduk(lat, lng) {
|
||||||
|
const nama = document.getElementById('inp-nama-penduduk')?.value?.trim();
|
||||||
|
if (!nama) { showToast('<i class="bi bi-exclamation"></i> Nama tidak boleh kosong!', '#e74c3c'); return; }
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('type', 'penduduk');
|
||||||
|
fd.append('nama_penduduk', nama);
|
||||||
|
fd.append('latitude', lat);
|
||||||
|
fd.append('longitude', lng);
|
||||||
|
|
||||||
|
const res = await fetch('ambil.php', { method: 'POST', body: fd });
|
||||||
|
const text = await res.text(); // Ambil teks mentah
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(text);
|
||||||
|
if (data.status === 'success') {
|
||||||
|
map.closePopup();
|
||||||
|
showToast(`<i class="bi bi-check-circle-fill"></i> ${data.message}`, '#2ecc71');
|
||||||
|
await loadData();
|
||||||
|
} else {
|
||||||
|
showToast(`<i class="bi bi-x-circle"></i> ${data.message}`, '#e74c3c');
|
||||||
|
}
|
||||||
|
} catch(parseErr) {
|
||||||
|
showToast(`<i class="bi bi-bug"></i> Error Server: ${text.substring(0, 50)}...`, '#e74c3c');
|
||||||
|
console.error('Response bukan JSON:', text);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
showToast('<i class="bi bi-wifi-off"></i> Gagal terhubung ke ambil.php', '#e74c3c');
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== BOOT =====
|
||||||
|
loadData();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
include 'koneksi.php';
|
||||||
|
|
||||||
|
if ($_POST) {
|
||||||
|
$nama = mysqli_real_escape_string($conn, $_POST['nama_jalan']);
|
||||||
|
$status = mysqli_real_escape_string($conn, $_POST['status']);
|
||||||
|
$panjang = mysqli_real_escape_string($conn, $_POST['panjang']);
|
||||||
|
// Sangat penting: gunakan mysqli_real_escape_string untuk GeoJSON
|
||||||
|
$geojson = mysqli_real_escape_string($conn, $_POST['geojson']);
|
||||||
|
|
||||||
|
$query = "INSERT INTO jalan (nama_jalan, status_jalan, panjang_m, geojson)
|
||||||
|
VALUES ('$nama', '$status', '$panjang', '$geojson')";
|
||||||
|
|
||||||
|
if (mysqli_query($conn, $query)) {
|
||||||
|
echo "<script>alert('Data Jalan Berhasil Disimpan!'); window.location='index.php';</script>";
|
||||||
|
} else {
|
||||||
|
echo "Error SQL: " . mysqli_error($conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
include 'koneksi.php';
|
||||||
|
|
||||||
|
if ($_POST) {
|
||||||
|
$id_parsil = mysqli_real_escape_string($conn, $_POST['id_parsil']);
|
||||||
|
$status = mysqli_real_escape_string($conn, $_POST['status']);
|
||||||
|
$luas = mysqli_real_escape_string($conn, $_POST['luas']);
|
||||||
|
$geojson = mysqli_real_escape_string($conn, $_POST['geojson']);
|
||||||
|
|
||||||
|
$query = "INSERT INTO parsil (id_parsil, status_milik, luas_m2, geojson)
|
||||||
|
VALUES ('$id_parsil', '$status', '$luas', '$geojson')";
|
||||||
|
|
||||||
|
if (mysqli_query($conn, $query)) {
|
||||||
|
echo "<script>alert('Data Parsil Berhasil Disimpan!'); window.location='index.php';</script>";
|
||||||
|
} else {
|
||||||
|
echo "Error SQL: " . mysqli_error($conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
include 'koneksi.php';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
$nama = mysqli_real_escape_string($conn, $_POST['nama']);
|
||||||
|
$wa = mysqli_real_escape_string($conn, $_POST['wa']);
|
||||||
|
$aktif = $_POST['aktif'];
|
||||||
|
$lat = $_POST['lat'];
|
||||||
|
$lng = $_POST['lng'];
|
||||||
|
|
||||||
|
$query = "INSERT INTO spbu (nama_spbu, no_wa, is_24jam, latitude, longitude)
|
||||||
|
VALUES ('$nama', '$wa', '$aktif', '$lat', '$lng')";
|
||||||
|
|
||||||
|
if (mysqli_query($conn, $query)) {
|
||||||
|
// Jika berhasil, balik lagi ke halaman peta
|
||||||
|
header("Location: index.php?status=sukses");
|
||||||
|
} else {
|
||||||
|
echo "Error: " . mysqli_error($conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
echo "PHP OK";
|
||||||
|
echo " | " . phpversion();
|
||||||
|
$conn = mysqli_connect("127.0.0.1", "root", "", "webgis");
|
||||||
|
if ($conn) {
|
||||||
|
echo " | MySQL OK";
|
||||||
|
} else {
|
||||||
|
echo " | MySQL GAGAL: " . mysqli_connect_error();
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
include 'koneksi.php';
|
||||||
|
|
||||||
|
if (isset($_POST['id']) && isset($_POST['lat']) && isset($_POST['lng'])) {
|
||||||
|
$id = mysqli_real_escape_string($conn, $_POST['id']);
|
||||||
|
$lat = mysqli_real_escape_string($conn, $_POST['lat']);
|
||||||
|
$lng = mysqli_real_escape_string($conn, $_POST['lng']);
|
||||||
|
|
||||||
|
$query = "UPDATE spbu SET latitude='$lat', longitude='$lng' WHERE id='$id'";
|
||||||
|
|
||||||
|
if (mysqli_query($conn, $query)) {
|
||||||
|
echo "Berhasil update posisi";
|
||||||
|
} else {
|
||||||
|
echo "Gagal: " . mysqli_error($conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
Reference in New Issue
Block a user