Files
webgis-poverty-mapping/index.php
T
2026-06-09 13:59:18 +00:00

374 lines
21 KiB
PHP

<?php
include 'auth.php';
// Hanya Admin Pemerintah yang bisa mengakses halaman ini
if (!isAdminPemerintah()) {
header("Location: dashboard.php");
exit;
}
include 'koneksi.php';
echo "<pre>";
$q = mysqli_query($conn, "SHOW TABLES");
while($r = mysqli_fetch_array($q)){
print_r($r);
}
echo "</pre>";
exit;
?>
<!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>
<hr style="border:none;border-top:1px solid rgba(255,255,255,0.08);margin:8px 0;">
<a href="dashboard.php" class="btn-mode" style="text-decoration:none;">
<i class="bi bi-speedometer2" style="color:#f39c12"></i> Dashboard
</a>
<a href="logout.php" class="btn-mode" style="text-decoration:none;" onclick="return confirm('Yakin ingin logout?')">
<i class="bi bi-box-arrow-right" style="color:#e74c3c"></i> Logout
</a>
</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>