res.json()).then(data => {
data.forEach(spbu => {
let targetLayer = (spbu.buka_24jam == '1') ? layerSPBU24 : layerSPBUNon24;
let marker = L.marker([spbu.lat, spbu.lng], { icon: (spbu.buka_24jam == '1') ? ikonHijau : ikonMerah, draggable: true }).addTo(targetLayer);
marker.bindPopup(`${spbu.nama_spbu}
WA: ${spbu.no_wa}
24 Jam: ${spbu.buka_24jam == '1' ? 'Ya' : 'Tidak'}
`);
marker.on('dragend', function(e) { updateLokasiSPBU(spbu.id, e.target.getLatLng().lat, e.target.getLatLng().lng); });
daftarMarker[spbu.id] = marker;
});
});
fetch('load_jalan.php').then(res => res.json()).then(data => {
data.forEach(jalan => {
let geojsonGroup = L.geoJSON(jalan.geojson, { style: { color: warnaJalan(jalan.status), weight: 4 } }).addTo(layerJalan);
let polylineLayer = geojsonGroup.getLayers()[0]; // Ambil layer aslinya
polylineLayer.bindPopup(`Jalan: ${jalan.nama_jalan}
Status: ${jalan.status}
Panjang: ${parseFloat(jalan.panjang_m).toFixed(2)} m
`);
// Tambahkan event click khusus untuk mode edit
polylineLayer.on('click', function(e) {
if (currentMode === 'edit_jalan') {
aktifkanEditJalan(jalan.id, jalan.nama_jalan);
}
});
daftarPolyline[jalan.id] = polylineLayer;
});
});
fetch('load_area.php').then(res => res.json()).then(data => {
data.forEach(area => {
let geojsonGroup = L.geoJSON(area.geojson, { style: { color: warnaPolygon(area.status_kepemilikan), fillOpacity: 0.4 } }).addTo(layerKavling);
let polygonLayer = geojsonGroup.getLayers()[0]; // Ambil layer aslinya
polygonLayer.bindPopup(`Kavling: ${area.nama_kavling}
Status: ${area.status_kepemilikan}
Luas: ${parseFloat(area.luas_m2).toFixed(2)} m²
`);
// Tambahkan event click khusus untuk mode edit
polygonLayer.on('click', function(e) {
if (currentMode === 'edit_kavling') {
aktifkanEditKavling(area.id, area.nama_kavling);
}
});
daftarPolygon[area.id] = polygonLayer;
});
});
}
loadDB();
// --- FUNGSI UNTUK ME-REFRESH PETA SECARA OTOMATIS (SOFT REFRESH) ---
function refreshPeta() {
// 1. Bersihkan semua layer dari peta
layerSPBU24.clearLayers();
layerSPBUNon24.clearLayers();
layerJalan.clearLayers();
layerKavling.clearLayers();
// 2. Bersihkan memori agar ID tidak menumpuk/ganda
for (let id in daftarMarker) delete daftarMarker[id];
for (let id in daftarPolyline) delete daftarPolyline[id];
for (let id in daftarPolygon) delete daftarPolygon[id];
// 3. Panggil ulang data terbaru dari database
loadDB();
}
// --- EVENT KLIK PETA ---
function onMapClick(e) {
if (currentMode === 'point') {
let formHtml = `
SPBU Baru
`;
popup.setLatLng(e.latlng).setContent(formHtml).openOn(map);
} else if (currentMode === 'polyline') {
tempLatLngs.push(e.latlng);
if (!tempPolyline) tempPolyline = L.polyline(tempLatLngs, {color: '#3498db', weight: 4}).addTo(map);
else tempPolyline.setLatLngs(tempLatLngs);
} else if (currentMode === 'polygon') {
tempPolygonLatLngs.push(e.latlng);
if (!tempPolygon) tempPolygon = L.polygon(tempPolygonLatLngs, {color: '#2ecc71', fillColor: '#2ecc71', fillOpacity: 0.4}).addTo(map);
else tempPolygon.setLatLngs(tempPolygonLatLngs);
}
}
map.on('click', onMapClick);
// --- LOGIKA SPBU ---
function simpanSPBU(event, lat, lng) {
event.preventDefault();
let dataKirim = { lat: lat, lng: lng, nama_spbu: document.getElementById('nama_spbu').value, no_wa: document.getElementById('no_wa').value, buka_24jam: document.querySelector('input[name="buka_24jam"]:checked').value };
fetch('simpan_spbu.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(dataKirim) })
.then(res => res.json()).then(data => {
if (data.status === 'success') {
Toast.fire({ icon: 'success', title: 'SPBU Berhasil Disimpan!' });
map.closePopup();
refreshPeta();
let targetLayerBaru = (dataKirim.buka_24jam === '1') ? layerSPBU24 : layerSPBUNon24;
let markerBaru = L.marker([lat, lng], { icon: (dataKirim.buka_24jam === '1') ? ikonHijau : ikonMerah, draggable: true }).addTo(targetLayerBaru);
markerBaru.bindPopup(`${dataKirim.nama_spbu}
WA: ${dataKirim.no_wa}
24 Jam: ${dataKirim.buka_24jam == '1' ? 'Ya' : 'Tidak'}
`);
markerBaru.on('dragend', function(e) { updateLokasiSPBU(data.id, e.target.getLatLng().lat, e.target.getLatLng().lng); });
daftarMarker[data.id] = markerBaru;
} else Toast.fire({ icon: 'error', title: 'Gagal Menyimpan SPBU!' });
});
}
function updateLokasiSPBU(id, latBaru, lngBaru) {
fetch('update_spbu.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: id, lat: latBaru, lng: lngBaru }) })
.then(res => res.json()).then(data => { if (data.status === 'success') Toast.fire({ icon: 'info', title: 'Posisi Diperbarui' }); });
}
function hapusSPBU(id) {
Swal.fire({ title: 'Hapus Titik SPBU?', text: "Data tidak dapat dikembalikan!", icon: 'warning', showCancelButton: true, confirmButtonColor: '#d33', cancelButtonColor: '#3085d6', confirmButtonText: 'Ya, Hapus!', cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
fetch('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') { Toast.fire({ icon: 'success', title: 'Titik SPBU Dihapus!' }); if (daftarMarker[id]) { map.removeLayer(daftarMarker[id]); delete daftarMarker[id]; } }
else Toast.fire({ icon: 'error', title: 'Gagal Menghapus!' });
});
}
});
}
// --- LOGIKA JALAN ---
function batalPolyline() { if (tempPolyline) { map.removeLayer(tempPolyline); tempPolyline = null; } tempLatLngs = []; }
function simpanPolyline() {
if (tempLatLngs.length < 2) return Toast.fire({ icon: 'warning', title: 'Klik minimal 2 titik di peta!' });
let nama = document.getElementById('input_nama_jalan').value, status = document.getElementById('input_status_jalan').value;
if (!nama) return Toast.fire({ icon: 'warning', title: 'Nama Jalan harus diisi!' });
let panjang = hitungPanjangJalan(tempLatLngs);
let dataKirim = { nama_jalan: nama, status: status, panjang_m: panjang, geojson: tempPolyline.toGeoJSON() };
fetch('simpan_jalan.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(dataKirim) })
.then(res => res.json()).then(data => {
if (data.status === 'success') {
Toast.fire({ icon: 'success', title: `Jalan Disimpan (${panjang.toFixed(0)}m)` });
tempPolyline.setStyle({color: warnaJalan(status)});
tempPolyline.bindPopup(`Jalan: ${nama}
Status: ${status}
Panjang: ${panjang.toFixed(2)} m
`).openPopup();
layerJalan.addLayer(tempPolyline);
map.removeLayer(tempPolyline);
tempPolyline.on('click', function(e) {
if (currentMode === 'edit_jalan') {
aktifkanEditJalan(data.id, nama);
}
});
daftarPolyline[data.id] = tempPolyline;
document.getElementById('input_nama_jalan').value = '';
refreshPeta();
// PERBAIKAN BUG: Hanya kosongkan variabel penampung sementara, jangan removeLayer dari map.
tempPolyline = null;
tempLatLngs = [];
} else Toast.fire({ icon: 'error', title: 'Gagal Menyimpan!' });
});
}
function hapusPolyline(id) {
Swal.fire({ title: 'Hapus Jalan?', text: "Data permanen dihapus!", icon: 'warning', showCancelButton: true, confirmButtonColor: '#d33', cancelButtonColor: '#3085d6', confirmButtonText: 'Ya, Hapus!'
}).then((result) => {
if (result.isConfirmed) {
fetch('hapus_jalan.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: id }) })
.then(res => res.json()).then(data => {
if (data.status === 'success') { Toast.fire({ icon: 'success', title: 'Jalan Dihapus!' }); if (daftarPolyline[id]) { map.removeLayer(daftarPolyline[id]); delete daftarPolyline[id]; } }
else Toast.fire({ icon: 'error', title: 'Gagal Menghapus!' });
});
}
});
}
// --- LOGIKA KAVLING ---
function batalPolygon() { if (tempPolygon) { map.removeLayer(tempPolygon); tempPolygon = null; } tempPolygonLatLngs = []; }
function simpanPolygon() {
if (tempPolygonLatLngs.length < 3) return Toast.fire({ icon: 'warning', title: 'Minimal 3 titik untuk area!' });
let nama = document.getElementById('input_nama_kavling').value, status = document.getElementById('input_status_tanah').value;
if (!nama) return Toast.fire({ icon: 'warning', title: 'Nama kavling harus diisi!' });
let luas = hitungLuasArea(tempPolygonLatLngs);
let dataKirim = { nama_kavling: nama, status: status, luas_m2: luas, geojson: tempPolygon.toGeoJSON() };
fetch('simpan_area.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(dataKirim) })
.then(res => res.json()).then(data => {
if (data.status === 'success') {
Toast.fire({ icon: 'success', title: `Area Disimpan (${luas.toFixed(0)}m²)` });
tempPolygon.setStyle({color: warnaPolygon(status), fillOpacity: 0.5});
tempPolygon.bindPopup(`Kavling: ${nama}
Status: ${status}
Luas: ${luas.toFixed(2)} m²
`).openPopup();
layerKavling.addLayer(tempPolygon);
map.removeLayer(tempPolygon);
tempPolygon.on('click', function(e) {
if (currentMode === 'edit_kavling') {
aktifkanEditKavling(data.id, nama);
}
});
daftarPolygon[data.id] = tempPolygon;
document.getElementById('input_nama_kavling').value = '';
refreshPeta();
// PERBAIKAN BUG: Sama seperti Polyline
tempPolygon = null;
tempPolygonLatLngs = [];
} else Toast.fire({ icon: 'error', title: 'Gagal Menyimpan!' });
});
}
function hapusPolygon(id) {
Swal.fire({ title: 'Hapus Kavling?', text: "Area ini akan dihapus permanen!", icon: 'warning', showCancelButton: true, confirmButtonColor: '#d33', cancelButtonColor: '#3085d6', confirmButtonText: 'Ya, Hapus!'
}).then((result) => {
if (result.isConfirmed) {
fetch('hapus_area.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: id }) })
.then(res => res.json()).then(data => {
if (data.status === 'success') { Toast.fire({ icon: 'success', title: 'Kavling Dihapus!' }); if (daftarPolygon[id]) { map.removeLayer(daftarPolygon[id]); delete daftarPolygon[id]; } }
else Toast.fire({ icon: 'error', title: 'Gagal Menghapus!' });
});
}
});
}
// ==========================================
// --- LOGIKA MODE EDIT POLYLINE (JALAN) ---
// ==========================================
function aktifkanEditJalan(id, namaJalan) {
let layer = daftarPolyline[id];
if (!layer) return;
// Jika sedang edit jalan lain, batalkan dulu
if (targetEditJalanId && targetEditJalanId !== id) {
batalkanEditJalan();
}
targetEditJalanId = id;
map.closePopup(); // Tutup popup info
// Tampilkan panel info dan tombol simpan di sidebar
document.getElementById('info-edit-jalan').style.display = 'block';
document.getElementById('nama-jalan-edit').innerText = "Edit: " + namaJalan;
document.getElementById('btn-simpan-edit-jalan').style.display = 'block';
// Aktifkan Leaflet.Geoman khusus pada layer ini
layer.pm.enable({
allowSelfIntersection: false,
});
}
function batalkanEditJalan() {
if (targetEditJalanId && daftarPolyline[targetEditJalanId]) {
daftarPolyline[targetEditJalanId].pm.disable();
}
targetEditJalanId = null;
document.getElementById('info-edit-jalan').style.display = 'none';
document.getElementById('btn-simpan-edit-jalan').style.display = 'none';
}
function simpanEditPolyline() {
if (!targetEditJalanId) return;
let layer = daftarPolyline[targetEditJalanId];
if (!layer) return;
// 1. Matikan mode edit
layer.pm.disable();
// 2. Kalkulasi ulang panjang
let latlngsBaru = layer.getLatLngs();
let panjangBaru = hitungPanjangJalan(latlngsBaru);
// 3. Konversi ke GeoJSON
let geojsonBaru = layer.toGeoJSON();
let dataKirim = {
id: targetEditJalanId,
panjang_m: panjangBaru,
geojson: geojsonBaru
};
fetch('update_jalan.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(dataKirim)
})
.then(res => res.json())
.then(data => {
if (data.status === 'success') {
Toast.fire({ icon: 'success', title: `Bentuk Jalan Diperbarui!\nPanjang: ${panjangBaru.toFixed(0)}m` });
batalkanEditJalan();
// Reload halaman secara otomatis setelah 1.5 detik agar data popup ikut ter-refresh
setTimeout(() => { window.location.reload(); }, 1500);
} else {
Toast.fire({ icon: 'error', title: 'Gagal memperbarui jalan: ' + data.message });
batalkanEditJalan();
}
}).catch(error => console.error('Error:', error));
}
// ===========================================
// --- LOGIKA MODE EDIT POLYGON (KAVLING) ---
// ===========================================
function aktifkanEditKavling(id, namaKavling) {
let layer = daftarPolygon[id];
if (!layer) return;
// Jika sedang edit kavling lain, batalkan dulu
if (targetEditKavlingId && targetEditKavlingId !== id) {
batalkanEditKavling();
}
targetEditKavlingId = id;
map.closePopup();
// Tampilkan panel info dan tombol simpan di sidebar
document.getElementById('info-edit-kavling').style.display = 'block';
document.getElementById('nama-kavling-edit').innerText = "Edit: " + namaKavling;
document.getElementById('btn-simpan-edit-kavling').style.display = 'block';
// Aktifkan Leaflet.Geoman khusus pada layer ini
layer.pm.enable({
allowSelfIntersection: false,
});
}
function batalkanEditKavling() {
if (targetEditKavlingId && daftarPolygon[targetEditKavlingId]) {
daftarPolygon[targetEditKavlingId].pm.disable();
}
targetEditKavlingId = null;
document.getElementById('info-edit-kavling').style.display = 'none';
document.getElementById('btn-simpan-edit-kavling').style.display = 'none';
}
function simpanEditPolygon() {
if (!targetEditKavlingId) return;
let layer = daftarPolygon[targetEditKavlingId];
if (!layer) return;
layer.pm.disable();
let latlngsBaru = layer.getLatLngs();
let luasBaru = hitungLuasArea(latlngsBaru);
let geojsonBaru = layer.toGeoJSON();
let dataKirim = {
id: targetEditKavlingId,
luas_m2: luasBaru,
geojson: geojsonBaru
};
fetch('update_area.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(dataKirim)
})
.then(res => res.json())
.then(data => {
if (data.status === 'success') {
Toast.fire({ icon: 'success', title: `Bentuk Kavling Diperbarui!\nLuas: ${luasBaru.toFixed(0)}m²` });
batalkanEditKavling();
setTimeout(() => { window.location.reload(); }, 1500);
} else {
Toast.fire({ icon: 'error', title: 'Gagal memperbarui kavling: ' + data.message });
batalkanEditKavling();
}
}).catch(error => console.error('Error:', error));
}