Initial commit WebGIS Poverty Mapping

This commit is contained in:
SatryaIrvannurYudha
2026-06-04 21:28:53 +07:00
commit fdf1e36cd3
18 changed files with 1333 additions and 0 deletions
+91
View File
@@ -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;
}
}
+4
View File
@@ -0,0 +1,4 @@
<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
include 'ambil.php';
+50
View File
@@ -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>";
?>
+22
View File
@@ -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";
}
+33
View File
@@ -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');
+32
View File
@@ -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;
+14
View File
@@ -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);
}
}
?>
+14
View File
@@ -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);
}
}
?>
+14
View File
@@ -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
View File
@@ -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: '&copy; <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>
+348
View File
@@ -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>
+6
View File
@@ -0,0 +1,6 @@
<?php
$host = "127.0.0.1";
$user = "root";
$pass = "";
$db = "webgis";
$conn = mysqli_connect($host, $user, $pass, $db);
+538
View File
@@ -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>
+20
View File
@@ -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);
}
}
?>
+19
View File
@@ -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);
}
}
?>
+21
View File
@@ -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);
}
}
?>
+9
View File
@@ -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();
}
+17
View File
@@ -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);
}
}
?>