Initial commit - Fresh Clean Code with User Manual README

This commit is contained in:
2026-06-11 08:03:34 +07:00
commit aa7be42b5d
5 changed files with 386 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
https://sigwebsite.free.je/SPBU_Marker/
+38
View File
@@ -0,0 +1,38 @@
# ??? Panduan Penggunaan Sistem Informasi Geografis (SIG) - SPBU Marker
Selamat datang di Panduan Pengguna aplikasi **Sistem Informasi Geografis Pemetaan SPBU**. Aplikasi ini dirancang untuk mempermudah Anda dalam melihat, menambah, mengubah, dan menghapus data marker SPBU secara interaktif di atas peta.
## ?? Link Akses Website
Aplikasi dapat diakses langsung melalui tautan berikut:
?? [Demo Live Website SPBU Marker](https://sigwebsite.free.je/SPBU_Marker/)
---
## ?? Fitur Utama & Cara Menggunakannya
### 1. Menampilkan Daftar & Fokus Lokasi SPBU
* **Cara Kerja:** Di sebelah kanan layar terdapat panel **"Daftar SPBU Terdaftar"** yang menampilkan semua nama, nomor registrasi, dan alamat SPBU yang ada di database.
* **Fitur Utama:** Jika Anda **mengklik salah satu item** pada daftar tersebut, kamera peta akan otomatis melakukan *fly-to* (meluncur/fokus) ke titik lokasi SPBU tersebut dengan tingkat perbesaran yang tinggi (*zoom-in*), dan form input di sebelah kanan akan otomatis berubah menjadi mode edit.
### 2. Menambah Data SPBU Baru (Lacak Alamat Otomatis)
* **Langkah 1:** Klik tombol hijau besar bertuliskan **"TAMBAH SPBU BARU"** di bawah peta. Kursor mouse pada area peta akan berubah menjadi tanda bidik (*crosshair*).
* **Langkah 2:** Cari lokasi yang diinginkan pada peta, lalu **klik sekali** pada titik tersebut. Sebuah pin (marker) sementara akan muncul.
* **Fitur Cerdas (Geocoding):** Sistem akan otomatis melacak koordinat (Latitude & Longitude) serta mengisi kolom alamat secara otomatis berdasarkan titik yang Anda klik menggunakan layanan *OpenStreetMap Nominatim*.
* **Langkah 3:** Lengkapi kolom nama SPBU, nomor register, dan tipe operasional (24 Jam atau Biasa) pada form di sebelah kanan.
* **Langkah 4:** Klik tombol **"SIMPAN DATA"** untuk memasukkannya ke database.
### 3. Mengubah / Mengedit Data SPBU (Interaktif)
Ada dua cara mudah untuk mengubah data yang sudah terdaftar:
* **Melalui Form:** Klik salah satu SPBU di daftar sebelah kanan, ubah informasi yang diinginkan pada form (seperti mengganti nama atau tipe), lalu klik **"UPDATE DATA SPBU"**.
* **Melalui Geser Pin (Drag & Drop):** Anda bisa langsung **mengklik dan menahan (drag)** ikon pin SPBU di atas peta, lalu menggesernya ke lokasi baru. Saat pin dilepas, sistem akan otomatis memperbarui koordinat posisi baru dan melacak ulang alamat terbarunya secara instan.
### 4. Menghapus Data SPBU
* **Cara Kerja:** Klik pin SPBU yang ingin dihapus di atas peta sampai muncul jendela kecil (*popup*).
* **Langkah:** Klik tombol merah bertuliskan **"Hapus"** di dalam popup tersebut. Sistem akan menampilkan konfirmasi keamanan sebelum benar-benar menghapus data tersebut dari server.
---
## ?? Indikator Warna Pin di Peta
Untuk mempermudah visualisasi, tanda pin di peta memiliki dua warna berbeda berdasarkan tipe operasionalnya:
* ?? **Pin Hijau:** Menandakan SPBU yang beroperasi penuh **24 Jam**.
* ?? **Pin Merah:** Menandakan SPBU dengan jam operasional **Biasa / Standar**.
+36
View File
@@ -0,0 +1,36 @@
<?php
// api_spbu.php
require_once 'config.php';
$method = $_SERVER['REQUEST_METHOD'];
$action = isset($_GET['action']) ? $_GET['action'] : '';
if ($method === 'GET' && $action === 'getAll') {
$result = $conn->query("SELECT * FROM spbu ORDER BY created_at DESC");
$data = [];
while ($row = $result->fetch_assoc()) { $data[] = $row; }
sendResponse(true, 'Data dimuat', $data);
}
elseif ($method === 'POST' && $action === 'create') {
$input = json_decode(file_get_contents('php://input'), true);
$stmt = $conn->prepare("INSERT INTO spbu (nama, no_spbu, alamat, tipe, latitude, longitude) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->bind_param("ssssdd", $input['nama'], $input['no_spbu'], $input['alamat'], $input['tipe'], $input['latitude'], $input['longitude']);
if ($stmt->execute()) sendResponse(true, 'Berhasil simpan', ['id' => $conn->insert_id]);
else sendResponse(false, 'Gagal simpan: ' . $stmt->error);
}
elseif ($method === 'PUT' && $action === 'update') {
$id = intval($_GET['id']);
$input = json_decode(file_get_contents('php://input'), true);
$stmt = $conn->prepare("UPDATE spbu SET nama=?, no_spbu=?, alamat=?, tipe=?, latitude=?, longitude=? WHERE id=?");
$stmt->bind_param("ssssddi", $input['nama'], $input['no_spbu'], $input['alamat'], $input['tipe'], $input['latitude'], $input['longitude'], $id);
if ($stmt->execute()) sendResponse(true, 'Berhasil update');
else sendResponse(false, 'Gagal update');
}
elseif ($method === 'DELETE' && $action === 'delete') {
$id = intval($_GET['id']);
$stmt = $conn->prepare("DELETE FROM spbu WHERE id=?");
$stmt->bind_param("i", $id);
if ($stmt->execute()) sendResponse(true, 'Berhasil hapus');
else sendResponse(false, 'Gagal hapus');
}
?>
+26
View File
@@ -0,0 +1,26 @@
<?php
// config.php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
$host = 'localhost';
$username = 'root';
$password = '';
$database = 'gis_management';
$port = 3307; // Sesuaikan port XAMPP Anda
$conn = new mysqli($host, $username, $password, $database, $port);
if ($conn->connect_error) {
die(json_encode(['success' => false, 'message' => 'Koneksi gagal: ' . $conn->connect_error]));
}
$conn->set_charset("utf8");
function sendResponse($success, $message, $data = null) {
echo json_encode(['success' => $success, 'message' => $message, 'data' => $data]);
exit;
}
?>
+285
View File
@@ -0,0 +1,285 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Sistem GIS SPBU - Mandiri</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* Mengatur tinggi peta agar serasi dengan sidebar */
#map { height: 650px; width: 100%; border-radius: 12px; border: 2px solid #dee2e6; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
/* Sidebar diperpanjang ke bawah */
.sidebar-panel {
background: white; padding: 20px; border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
height: 730px; /* Tinggi total sidebar ditambah */
display: flex; flex-direction: column;
}
/* Kolom Daftar SPBU diperpanjang maksimal */
.data-list {
flex-grow: 1;
overflow-y: auto;
margin-top: 10px;
padding-right: 8px;
border-top: 1px solid #eee;
padding-top: 10px;
}
/* Desain item daftar lebih elegan */
.data-item {
background: #ffffff; padding: 15px; margin-bottom: 12px; border-radius: 10px;
cursor: pointer; border: 1px solid #e9ecef; border-left: 6px solid #0d6efd;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(0,0,0,0.02);
}
.data-item:hover { background: #f1f4f9; transform: translateX(5px); border-color: #0d6efd; }
.data-item strong { color: #333; display: block; margin-bottom: 3px; }
.data-item small { color: #6c757d; font-size: 0.85rem; display: block; line-height: 1.3; }
.is-editing { border-left-color: #ffc107 !important; background: #fffdf5 !important; border-color: #ffc107; }
.custom-pin { filter: drop-shadow(0 3px 3px rgba(0,0,0,0.4)); }
/* Scrollbar kustom agar terlihat modern */
.data-list::-webkit-scrollbar { width: 6px; }
.data-list::-webkit-scrollbar-track { background: #f1f1f1; }
.data-list::-webkit-scrollbar-thumb { background: #ccc; border-radius: 10px; }
</style>
</head>
<body class="bg-light">
<div class="container-fluid py-4">
<div class="row g-4">
<div class="col-md-8">
<div id="map" class="mb-3"></div>
<div class="bg-white p-3 rounded shadow-sm d-flex justify-content-between align-items-center">
<button class="btn btn-success px-4 py-2 fw-bold shadow-sm" onclick="startAdding()">
<i class="fas fa-plus-circle me-2"></i> TAMBAH SPBU BARU
</button>
<div class="text-muted small">
<i class="fas fa-info-circle text-primary me-1"></i>
<b>Navigasi:</b> Klik item di daftar untuk Edit. Geser pin di peta untuk pindah lokasi.
</div>
</div>
</div>
<div class="col-md-4">
<div class="sidebar-panel">
<div id="formSection">
<h5 id="formTitle" class="fw-bold mb-3 text-primary d-flex justify-content-between">
<span><i class="fas fa-edit me-2"></i>Form SPBU</span>
<button class="btn btn-sm btn-link p-0 text-decoration-none" onclick="resetForm()">Reset</button>
</h5>
<form id="spbuForm">
<input type="hidden" id="spbuId">
<div class="mb-2">
<label class="small fw-bold text-secondary">Nama SPBU</label>
<input type="text" id="namaSpbu" class="form-control form-control-sm border-2" placeholder="Contoh: SPBU 64.001" required>
</div>
<div class="row mb-2 g-2">
<div class="col-7">
<label class="small fw-bold text-secondary">No. Register</label>
<input type="text" id="noSpbu" class="form-control form-control-sm" placeholder="ID SPBU" required>
</div>
<div class="col-5">
<label class="small fw-bold text-secondary">Tipe</label>
<select id="tipeSpbu" class="form-select form-select-sm">
<option value="24jam">24 Jam</option>
<option value="tidak">Biasa</option>
</select>
</div>
</div>
<div class="mb-2">
<label class="small fw-bold text-secondary">Alamat (Geocode Otomatis)</label>
<textarea id="alamatSpbu" class="form-control form-control-sm" rows="2" placeholder="Klik lokasi di peta..."></textarea>
</div>
<div class="mb-3">
<label class="small fw-bold text-secondary">Koordinat (Lat, Lng)</label>
<input type="text" id="koordinatSpbu" class="form-control form-control-sm bg-light text-muted" readonly>
</div>
<button type="submit" id="btnSubmit" class="btn btn-primary w-100 fw-bold shadow-sm py-2">SIMPAN DATA</button>
</form>
</div>
<div class="mt-4 d-flex justify-content-between align-items-center">
<h6 class="fw-bold mb-0"><i class="fas fa-list-ul me-2"></i>Daftar SPBU Terdaftar</h6>
<span id="countBadges" class="badge bg-secondary rounded-pill">0</span>
</div>
<div id="spbuList" class="data-list">
</div>
</div>
</div>
</div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
var map = L.map('map').setView([-0.026330, 109.342519], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
var spbuGroup = L.layerGroup().addTo(map);
var tempMarker = null;
var isAddingMode = false;
function getIcon(tipe) {
const color = tipe === '24jam' ? '#198754' : '#dc3545';
return L.divIcon({
html: `<i class="fas fa-map-marker-alt fa-2x custom-pin" style="color: ${color};"></i>`,
className: 'custom-div-icon',
iconSize: [30, 30],
iconAnchor: [15, 30],
popupAnchor: [0, -30]
});
}
async function reverseGeocode(lat, lng) {
const target = document.getElementById('alamatSpbu');
target.value = "Sedang melacak alamat...";
try {
const response = await fetch(`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lng}`);
const data = await response.json();
target.value = data.display_name || "Alamat tidak ditemukan di sistem";
} catch (e) {
target.value = "Koneksi ke layanan alamat terputus";
}
}
async function loadData() {
const res = await fetch('api_spbu.php?action=getAll');
const json = await res.json();
spbuGroup.clearLayers();
const listContainer = document.getElementById('spbuList');
listContainer.innerHTML = '';
document.getElementById('countBadges').innerText = json.data.length;
json.data.forEach(spbu => {
const marker = L.marker([spbu.latitude, spbu.longitude], {
icon: getIcon(spbu.tipe),
draggable: true
}).addTo(spbuGroup);
marker.on('dragend', async function(e) {
const pos = e.target.getLatLng();
if(document.getElementById('spbuId').value == spbu.id) {
document.getElementById('koordinatSpbu').value = `${pos.lat.toFixed(6)}, ${pos.lng.toFixed(6)}`;
reverseGeocode(pos.lat, pos.lng);
}
await saveUpdate(spbu.id, spbu.nama, spbu.no_spbu, spbu.alamat, spbu.tipe, pos.lat, pos.lng);
});
marker.bindPopup(`<b>${spbu.nama}</b><br><button class="btn btn-danger btn-sm mt-2 w-100" onclick="deleteData(${spbu.id})">Hapus</button>`);
const item = document.createElement('div');
item.className = 'data-item';
item.id = `item-${spbu.id}`;
item.innerHTML = `
<strong>${spbu.nama}</strong>
<small><i class="fas fa-tag me-1"></i> No: ${spbu.no_spbu}</small>
<small><i class="fas fa-map-marked-alt me-1"></i> ${spbu.alamat ? spbu.alamat.substring(0, 60) + '...' : 'Alamat belum diatur'}</small>
`;
item.onclick = () => prepareEdit(spbu, marker);
listContainer.appendChild(item);
});
}
function startAdding() {
resetForm();
isAddingMode = true;
document.getElementById('map').style.cursor = 'crosshair';
}
map.on('click', function(e) {
if (!isAddingMode) return;
const {lat, lng} = e.latlng;
document.getElementById('koordinatSpbu').value = `${lat.toFixed(6)}, ${lng.toFixed(6)}`;
reverseGeocode(lat, lng);
if (tempMarker) map.removeLayer(tempMarker);
tempMarker = L.marker([lat, lng], { draggable: true }).addTo(map);
tempMarker.on('dragend', function(ev) {
const p = ev.target.getLatLng();
document.getElementById('koordinatSpbu').value = `${p.lat.toFixed(6)}, ${p.lng.toFixed(6)}`;
reverseGeocode(p.lat, p.lng);
});
isAddingMode = false;
document.getElementById('map').style.cursor = '';
});
function prepareEdit(spbu, marker) {
document.querySelectorAll('.data-item').forEach(i => i.classList.remove('is-editing'));
document.getElementById(`item-${spbu.id}`).classList.add('is-editing');
document.getElementById('formTitle').innerHTML = '<span><i class="fas fa-edit me-2"></i>Edit SPBU</span><button class="btn btn-sm btn-link p-0 text-decoration-none" onclick="resetForm()">Batal</button>';
document.getElementById('spbuId').value = spbu.id;
document.getElementById('namaSpbu').value = spbu.nama;
document.getElementById('noSpbu').value = spbu.no_spbu;
document.getElementById('alamatSpbu').value = spbu.alamat || '';
document.getElementById('tipeSpbu').value = spbu.tipe;
document.getElementById('koordinatSpbu').value = `${spbu.latitude}, ${spbu.longitude}`;
document.getElementById('btnSubmit').innerText = "UPDATE DATA SPBU";
document.getElementById('btnSubmit').className = "btn btn-warning w-100 fw-bold py-2";
map.flyTo([spbu.latitude, spbu.longitude], 17);
marker.openPopup();
}
document.getElementById('spbuForm').onsubmit = async (e) => {
e.preventDefault();
const id = document.getElementById('spbuId').value;
const coords = document.getElementById('koordinatSpbu').value.split(', ');
if(!coords[0]) return alert("Tentukan lokasi di peta terlebih dahulu!");
const data = {
nama: document.getElementById('namaSpbu').value,
no_spbu: document.getElementById('noSpbu').value,
alamat: document.getElementById('alamatSpbu').value,
tipe: document.getElementById('tipeSpbu').value,
latitude: coords[0],
longitude: coords[1]
};
const url = id ? `api_spbu.php?action=update&id=${id}` : `api_spbu.php?action=create`;
await fetch(url, { method: id ? 'PUT' : 'POST', body: JSON.stringify(data) });
resetForm();
loadData();
};
async function saveUpdate(id, nama, no, alamat, tipe, lat, lng) {
await fetch(`api_spbu.php?action=update&id=${id}`, {
method: 'PUT',
body: JSON.stringify({ nama, no_spbu: no, alamat, tipe, latitude: lat, longitude: lng })
});
}
async function deleteData(id) {
if (confirm('Apakah Anda yakin ingin menghapus data SPBU ini dari sistem?')) {
await fetch(`api_spbu.php?action=delete&id=${id}`, { method: 'DELETE' });
loadData();
resetForm();
}
}
function resetForm() {
document.getElementById('spbuForm').reset();
document.getElementById('spbuId').value = '';
document.getElementById('formTitle').innerHTML = '<span><i class="fas fa-edit me-2"></i>Form SPBU</span>';
document.getElementById('btnSubmit').innerText = "SIMPAN DATA";
document.getElementById('btnSubmit').className = "btn btn-primary w-100 fw-bold py-2";
document.querySelectorAll('.data-item').forEach(i => i.classList.remove('is-editing'));
if (tempMarker) map.removeLayer(tempMarker);
isAddingMode = false;
document.getElementById('map').style.cursor = '';
}
loadData();
</script>
</body>
</html>