let currentMarker = null; let userMarker = null; let userLat = null; let userLng = null; let markerData = {}; let routingControl = null; let modeAdmin = true; let currentObjekLayer = null; let sedangMenggambar = false; const markerHijau = new L.Icon({ iconUrl: "https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-green.png", shadowUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png", iconSize: [25, 41], iconAnchor: [12, 41], }); const markerMerah = new L.Icon({ iconUrl: "https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-red.png", shadowUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png", iconSize: [25, 41], iconAnchor: [12, 41], }); // 1. Inisialisasi Peta let map = L.map("map").setView([-0.0263, 109.3425], 13); L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png").addTo(map); let markerGroup = L.layerGroup().addTo(map); let drawnItems = new L.FeatureGroup(); map.addLayer(drawnItems); let drawControl = new L.Control.Draw({ draw: { marker: false, polyline: true, polygon: true, rectangle: false, circle: false, circlemarker: false, }, edit: { featureGroup: drawnItems, }, }); map.addControl(drawControl); // TAMBAH DI SINI map.on(L.Draw.Event.DRAWSTART, function () { sedangMenggambar = true; }); map.on(L.Draw.Event.DRAWSTOP, function () { sedangMenggambar = false; }); console.log("draw berhasil"); // 2. Load Marker Dari Database loadSPBU(); loadObjekPeta(); function loadSPBU() { markerGroup.clearLayers(); markerData = {}; currentMarker = null; fetch("api/get_spbu.php") .then((res) => res.json()) .then((data) => { data.forEach((spbu) => { let marker = L.marker([spbu.latitude, spbu.longitude], { icon: spbu.buka_24_jam == 1 ? markerHijau : markerMerah, draggable: false, }).addTo(markerGroup); marker.spbuData = spbu; markerData[spbu.id] = marker; marker.on("dragend", function () { let pos = marker.getLatLng(); let latInput = document.getElementById("edit_lat"); let lngInput = document.getElementById("edit_lng"); if (latInput && lngInput) { latInput.value = pos.lat; lngInput.value = pos.lng; } alert("Posisi marker berubah, klik Simpan untuk menyimpan perubahan"); }); marker.bindPopup(`
${ spbu.foto ? ` ` : "" }

${spbu.nama_spbu}

${spbu.alamat}

Status : ${statusSPBU(spbu)}

Jam : ${spbu.buka_24_jam == 1 ? "24 Jam" : spbu.jam_buka + " - " + spbu.jam_tutup}

Lat : ${spbu.latitude}

Lng : ${spbu.longitude}

${ modeAdmin ? `
` : "" }
`); }); }); } function statusSPBU(spbu) { if (spbu.buka_24_jam == 1) { return ` BUKA 24 JAM `; } if (!spbu.jam_buka || !spbu.jam_tutup) { return ` Jam belum diatur `; } let now = new Date().toTimeString().substring(0, 5); let buka = spbu.jam_buka.substring(0, 5); let tutup = spbu.jam_tutup.substring(0, 5); let bukaStatus = false; if (buka < tutup) { bukaStatus = now >= buka && now <= tutup; } else { bukaStatus = now >= buka || now <= tutup; } return bukaStatus ? `BUKA` : `TUTUP`; } map.on("click", function (e) { if (!modeAdmin) { return; } if (sedangMenggambar) { return; } if ( map._container.classList.contains("leaflet-draw-draw-polygon") || map._container.classList.contains("leaflet-draw-draw-polyline") ) { return; } let lat = e.latlng.lat; let lng = e.latlng.lng; L.popup() .setLatLng(e.latlng) .setContent( `

Tambah SPBU

`, ) .openOn(map); }); document.addEventListener("change", function (e) { if (e.target.id !== "buka_24_jam") return; let checked = e.target.checked; document.getElementById("jam_buka").disabled = checked; document.getElementById("jam_tutup").disabled = checked; }); function simpanSPBU() { let nama = document.getElementById("nama").value; let alamat = document.getElementById("alamat").value; let lat = document.getElementById("lat").value; let lng = document.getElementById("lng").value; let formData = new FormData(); formData.append("nama", nama); formData.append("alamat", alamat); formData.append("lat", lat); formData.append("lng", lng); let jamBuka = document.getElementById("jam_buka").value; let jamTutup = document.getElementById("jam_tutup").value; let buka24Jam = document.getElementById("buka_24_jam").checked ? 1 : 0; formData.append("jam_buka", jamBuka); formData.append("jam_tutup", jamTutup); formData.append("buka_24_jam", buka24Jam); let foto = document.getElementById("foto"); if (foto && foto.files.length > 0) { formData.append("foto", foto.files[0]); } fetch("api/tambah_spbu.php", { method: "POST", body: formData, }) .then((res) => res.json()) .then((data) => { if (data.status == "success") { alert("Data berhasil disimpan"); map.closePopup(); loadSPBU(); loadObjekPeta(); } }); } function openEdit(spbu) { if (!modeAdmin) { return; } if (currentMarker) { currentMarker.dragging.disable(); } document.getElementById("sidebar").classList.remove("hidden"); document.getElementById("edit_id").value = spbu.id; document.getElementById("edit_nama").value = spbu.nama_spbu; document.getElementById("edit_alamat").value = spbu.alamat; document.getElementById("edit_foto").value = ""; let preview = document.getElementById("preview_edit_foto"); if (spbu.foto) { preview.src = "uploads/spbu/" + spbu.foto + "?t=" + Date.now(); preview.classList.remove("hidden"); } else { preview.src = ""; preview.classList.add("hidden"); } document.getElementById("edit_jam_buka").value = spbu.jam_buka || ""; document.getElementById("edit_jam_tutup").value = spbu.jam_tutup || ""; document.getElementById("edit_24jam").checked = spbu.buka_24_jam == 1; document.getElementById("edit_jam_buka").disabled = spbu.buka_24_jam == 1; document.getElementById("edit_jam_tutup").disabled = spbu.buka_24_jam == 1; document.getElementById("edit_lat").value = spbu.latitude; document.getElementById("edit_lng").value = spbu.longitude; currentMarker = markerData[spbu.id]; if (currentMarker) { currentMarker.dragging.enable(); } } function openEditById(id) { if (!modeAdmin) { return; } let marker = markerData[id]; if (!marker) return; openEdit(marker.spbuData); } function tutupSidebar() { document.getElementById("sidebar").classList.add("hidden"); if (currentMarker) { currentMarker.dragging.disable(); } currentMarker = null; } function updateSPBU() { if (!modeAdmin) { return; } let formData = new FormData(); formData.append("id", document.getElementById("edit_id").value); formData.append("nama", document.getElementById("edit_nama").value); formData.append("alamat", document.getElementById("edit_alamat").value); formData.append("lat", document.getElementById("edit_lat").value); formData.append("lng", document.getElementById("edit_lng").value); let foto = document.getElementById("edit_foto"); if (foto.files.length > 0) { formData.append("foto", foto.files[0]); } formData.append("jam_buka", document.getElementById("edit_jam_buka").value); formData.append("jam_tutup", document.getElementById("edit_jam_tutup").value); formData.append( "buka_24_jam", document.getElementById("edit_24jam").checked ? 1 : 0, ); fetch("api/edit_spbu.php", { method: "POST", body: formData, }) .then((res) => res.json()) .then((data) => { if (data.status == "success") { alert("Data berhasil diperbarui"); tutupSidebar(); loadSPBU(); loadObjekPeta(); } }); } function hapusSPBU(id) { if (!modeAdmin) { return; } if (!confirm("Yakin hapus data ini?")) { return; } fetch("api/hapus_spbu.php", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ id: id, }), }) .then((res) => res.json()) .then((data) => { if (data.status == "success") { alert("Data berhasil dihapus"); loadSPBU(); loadObjekPeta(); } }); } document.addEventListener("keyup", function (e) { if (e.target.id !== "search_spbu") return; let keyword = e.target.value.toLowerCase(); markerGroup.eachLayer(function (marker) { let nama = (marker.spbuData.nama_spbu || "").toLowerCase(); if (nama.includes(keyword)) { map.setView(marker.getLatLng(), 18); marker.openPopup(); } }); }); function cariSPBU() { let keyword = document.getElementById("search_spbu").value.toLowerCase(); let ditemukan = false; markerGroup.eachLayer(function (marker) { let nama = (marker.spbuData.nama_spbu || "").toLowerCase(); if (nama.includes(keyword)) { ditemukan = true; map.setView(marker.getLatLng(), 18); marker.openPopup(); } }); if (!ditemukan) { alert("SPBU tidak ditemukan"); } } function lokasiSaya() { if (!navigator.geolocation) { alert("Browser tidak mendukung geolocation"); return; } navigator.geolocation.getCurrentPosition( function (position) { let lat = position.coords.latitude; let lng = position.coords.longitude; userLat = lat; userLng = lng; if (userMarker) { map.removeLayer(userMarker); } userMarker = L.circleMarker([lat, lng], { radius: 10, color: "#2563eb", fillColor: "#2563eb", fillOpacity: 1, }).addTo(map); userMarker.bindPopup(` Lokasi Anda
Lat : ${lat}
Lng : ${lng} `); userMarker.openPopup(); map.setView([lat, lng], 16); }, function () { alert("Lokasi tidak dapat diakses"); }, ); } function hitungJarak(lat1, lon1, lat2, lon2) { const R = 6371; const dLat = ((lat2 - lat1) * Math.PI) / 180; const dLon = ((lon2 - lon1) * Math.PI) / 180; const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; } function cariTerdekat() { if (userLat === null) { alert("Klik Lokasi Saya terlebih dahulu"); return; } let terdekat = null; let jarakMin = Infinity; markerGroup.eachLayer(function (marker) { let lat = marker.getLatLng().lat; let lng = marker.getLatLng().lng; let jarak = hitungJarak(userLat, userLng, lat, lng); if (jarak < jarakMin) { jarakMin = jarak; terdekat = marker; } }); if (terdekat) { map.setView(terdekat.getLatLng(), 18); terdekat.openPopup(); alert( "SPBU terdekat: " + terdekat.spbuData.nama_spbu + "\nJarak: " + jarakMin.toFixed(2) + " km", ); } } function getMarkerTerdekat() { let terdekat = null; let jarakMin = Infinity; markerGroup.eachLayer(function (marker) { let lat = marker.getLatLng().lat; let lng = marker.getLatLng().lng; let jarak = hitungJarak(userLat, userLng, lat, lng); if (jarak < jarakMin) { jarakMin = jarak; terdekat = marker; } }); return terdekat; } function buatRuteTerdekat() { if (userLat === null) { alert("Klik Lokasi Saya terlebih dahulu"); return; } let marker = getMarkerTerdekat(); if (!marker) { alert("Tidak ada SPBU ditemukan"); return; } if (routingControl) { map.removeControl(routingControl); } routingControl = L.Routing.control({ show: false, collapsible: true, waypoints: [ L.latLng(userLat, userLng), L.latLng(marker.getLatLng().lat, marker.getLatLng().lng), ], router: L.Routing.osrmv1({ serviceUrl: "https://router.project-osrm.org/route/v1", }), routeWhileDragging: false, addWaypoints: false, draggableWaypoints: false, }).addTo(map); } document.addEventListener("change", function (e) { if (e.target.id !== "foto") return; let file = e.target.files[0]; if (!file) return; let preview = document.getElementById("previewFoto"); preview.src = URL.createObjectURL(file); preview.classList.remove("hidden"); }); document.addEventListener("change", function (e) { if (e.target.id !== "edit_foto") return; let file = e.target.files[0]; if (!file) return; let preview = document.getElementById("preview_edit_foto"); preview.src = URL.createObjectURL(file); preview.classList.remove("hidden"); }); document.addEventListener("change", function (e) { if (e.target.id !== "edit_24jam") return; let checked = e.target.checked; document.getElementById("edit_jam_buka").disabled = checked; document.getElementById("edit_jam_tutup").disabled = checked; }); function isBuka(spbu) { if (spbu.buka_24_jam == 1) { return true; } if (!spbu.jam_buka || !spbu.jam_tutup) { return false; } let now = new Date().toTimeString().substring(0, 5); let buka = spbu.jam_buka.substring(0, 5); let tutup = spbu.jam_tutup.substring(0, 5); if (buka < tutup) { return now >= buka && now <= tutup; } return now >= buka || now <= tutup; } function filterSPBU() { let filter = document.getElementById("filter_spbu").value; markerGroup.clearLayers(); Object.values(markerData).forEach(function (marker) { let spbu = marker.spbuData; let tampil = false; if (filter == "all") { tampil = true; } if (filter == "24" && spbu.buka_24_jam == 1) { tampil = true; } if (filter == "non24" && spbu.buka_24_jam == 0) { tampil = true; } if (filter == "buka" && isBuka(spbu)) { tampil = true; } if (filter == "tutup" && !isBuka(spbu)) { tampil = true; } if (tampil) { marker.addTo(markerGroup); } }); } function toggleMode() { modeAdmin = !modeAdmin; let btn = document.getElementById("btnMode"); let banner = document.getElementById("bannerMode"); if (modeAdmin) { banner.classList.add("hidden"); btn.innerHTML = "Pindah ke Mode Lihat"; btn.classList.remove("bg-green-600"); btn.classList.add("bg-blue-600"); status.className = "bg-blue-100 text-blue-700 px-3 py-2 rounded font-semibold"; } else { banner.classList.remove("hidden"); btn.innerHTML = "Masuk Mode Admin"; btn.classList.remove("bg-blue-600"); btn.classList.add("bg-yellow-600"); status.innerHTML = "View"; status.className = "bg-yellow-100 text-yellow-700 px-3 py-2 rounded font-semibold"; tutupSidebar(); map.closePopup(); } loadSPBU(); loadObjekPeta(); updateModeUI(); } function updateModeUI() { let status = document.getElementById("statusMode"); let banner = document.getElementById("bannerMode"); if (modeAdmin) { status.innerHTML = "🔓 ADMIN"; banner.classList.add("hidden"); } else { status.innerHTML = "👁️ VIEW"; banner.classList.remove("hidden"); } } function loadObjekPeta() { fetch("api/get_objek.php") .then((res) => res.json()) .then((data) => { drawnItems.clearLayers(); data.forEach((obj) => { let geo = JSON.parse(obj.geojson); let layer = L.geoJSON(geo); layer.eachLayer(function (l) { l.objekId = obj.id; l.on("click", function (e) { L.DomEvent.stopPropagation(e); l.bindPopup(` ${obj.nama || "-"}
${obj.deskripsi || "-"} `); l.openPopup(); }); l.on("dblclick", function (e) { L.DomEvent.stopPropagation(e); if (!modeAdmin) { return; } openObjekSidebar(obj, l); }); drawnItems.addLayer(l); }); }); }); } map.on(L.Draw.Event.CREATED, function (e) { let layer = e.layer; drawnItems.addLayer(layer); currentObjekLayer = layer; document.getElementById("sidebarObjek").classList.remove("hidden"); document.getElementById("objek_id").value = ""; document.getElementById("objek_nama").value = ""; document.getElementById("objek_deskripsi").value = ""; document.getElementById("objek_tipe").value = layer.toGeoJSON().geometry.type; }); map.on("draw:edited", function (e) { e.layers.eachLayer(function (layer) { let geojson = JSON.stringify(layer.toGeoJSON()); fetch("api/update_objek.php", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: "id=" + layer.objekId + "&geojson=" + encodeURIComponent(geojson), }); }); }); map.on("draw:deleted", function (e) { e.layers.eachLayer(function (layer) { fetch("api/hapus_objek.php", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: "id=" + layer.objekId, }); }); }); function openObjekSidebar(obj, layer) { currentObjekLayer = layer; document.getElementById("sidebarObjek").classList.remove("hidden"); document.getElementById("objek_id").value = obj.id; document.getElementById("objek_nama").value = obj.nama || ""; document.getElementById("objek_deskripsi").value = obj.deskripsi || ""; document.getElementById("objek_tipe").value = obj.tipe; } function tutupSidebarObjek() { document.getElementById("sidebarObjek").classList.add("hidden"); currentObjekLayer = null; } function updateObjek() { let id = document.getElementById("objek_id").value; let nama = document.getElementById("objek_nama").value; let deskripsi = document.getElementById("objek_deskripsi").value; let tipe = document.getElementById("objek_tipe").value; if (id == "") { let geojson = JSON.stringify(currentObjekLayer.toGeoJSON()); fetch("api/simpan_objek.php", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: "nama=" + encodeURIComponent(nama) + "&deskripsi=" + encodeURIComponent(deskripsi) + "&tipe=" + tipe + "&geojson=" + encodeURIComponent(geojson), }) .then((res) => res.json()) .then((data) => { alert("Objek berhasil disimpan"); tutupSidebarObjek(); loadObjekPeta(); }); } else { fetch("api/update_objek.php", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: "id=" + id + "&nama=" + encodeURIComponent(nama) + "&deskripsi=" + encodeURIComponent(deskripsi), }) .then((res) => res.json()) .then((data) => { alert("Objek berhasil diperbarui"); tutupSidebarObjek(); loadObjekPeta(); }); } }