// ========================================== // 1. INISIALISASI PETA & LAYER // ========================================== var map = L.map('map', { zoomControl: false }).setView([-0.0227, 109.3425], 14); // Zoom control di kanan bawah (tampilan modern) L.control.zoom({ position: 'bottomright' }).addTo(map); // OpenStreetMap Tile Layer L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(map); // Layer Groups var jalanLayer = L.layerGroup().addTo(map); var parsilLayer = L.layerGroup().addTo(map); var drawnItems = new L.FeatureGroup().addTo(map); // Layer Control (Checkbox di kanan atas) var overlayMaps = { "🛣️ Jaringan Jalan": jalanLayer, "🏘️ Parsil Bangunan": parsilLayer }; L.control.layers(null, overlayMaps, { collapsed: true, position: 'bottomleft' }).addTo(map); // Global Variables var databasePencarian = []; var sidebarDrawMode = null; // 'polyline' | 'polygon' var sidebarKoordinat = ""; var sidebarDrawHandler = null; // ========================================== // 2. FUNGSI LOAD DATA DARI DATABASE // ========================================== function loadJalan() { jalanLayer.clearLayers(); // Reset database pencarian khusus kategori Jalan databasePencarian = databasePencarian.filter(item => item.kategori !== 'Jalan'); fetch('load.php?jenis=jalan') .then(res => res.json()) .then(data => { data.forEach(item => { try { var geom = JSON.parse(item.koordinat); // Warna berdasarkan status jalan var warna = item.status === 'Jalan Nasional' ? '#ef4444' // Red : item.status === 'Jalan Provinsi' ? '#fbbf24' // Yellow/Amber : '#10b981'; // Green (Kabupaten) var viewHTML = ` `; var layerJalan = L.geoJSON(geom, { style: { color: warna, weight: 5, opacity: 0.85 } }).bindPopup(viewHTML).addTo(jalanLayer); layerJalan.on('click', function () { map.fitBounds(layerJalan.getBounds(), { padding: [50, 50] }); }); databasePencarian.push({ namaLower: item.nama_jalan.toLowerCase(), namaAsli: item.nama_jalan, kategori: 'Jalan', ikon: '🛣️', tipe: 'area', layer: layerJalan }); } catch (error) { console.error("Error parse koordinat jalan ID: " + item.id, error); } }); }) .catch(e => console.log('Belum ada data Jalan', e)); } function loadParsil() { parsilLayer.clearLayers(); // Reset database pencarian khusus kategori Parsil databasePencarian = databasePencarian.filter(item => item.kategori !== 'Parsil'); fetch('load.php?jenis=parsil') .then(res => res.json()) .then(data => { data.forEach(item => { try { var geom = JSON.parse(item.koordinat); // Warna berdasarkan status sertifikat tanah var warna = item.status === 'SHM' ? '#10b981' // Green : item.status === 'HGB' ? '#3b82f6' // Blue : item.status === 'HGU' ? '#8b5cf6' // Purple : '#f59e0b'; // Orange (HP) var viewHTML = ` `; var layerParsil = L.geoJSON(geom, { style: { fillColor: warna, color: warna, weight: 2, fillOpacity: 0.45 } }).bindPopup(viewHTML).addTo(parsilLayer); layerParsil.on('click', function () { map.fitBounds(layerParsil.getBounds(), { padding: [50, 50] }); }); databasePencarian.push({ namaLower: item.nama_pemilik.toLowerCase(), namaAsli: item.nama_pemilik, kategori: 'Parsil', ikon: '🏘️', tipe: 'area', layer: layerParsil }); } catch (error) { console.error("Error parse koordinat parsil ID: " + item.id, error); } }); }) .catch(e => console.log('Belum ada data Parsil', e)); } // Load data awal loadJalan(); loadParsil(); // ========================================== // 3. EDIT & UPDATE DATA // ========================================== function formEditJalan(id, nama, status) { setTimeout(function () { var container = document.getElementById(`popup-jalan-${id}`); if (!container) return; container.innerHTML = ` `; }, 50); } function formEditParsil(id, nama, status) { setTimeout(function () { var container = document.getElementById(`popup-parsil-${id}`); if (!container) return; container.innerHTML = ` `; }, 50); } function updateData(e, tabel, id) { e.preventDefault(); var formData = new FormData(); formData.append('tabel', tabel); formData.append('id', id); if (tabel === 'jalan') { formData.append('nama_jalan', document.getElementById('edit_nama').value); formData.append('status', document.getElementById('edit_status').value); } else if (tabel === 'parsil') { formData.append('nama_pemilik', document.getElementById('edit_nama').value); formData.append('status', document.getElementById('edit_status').value); } fetch('update_data.php', { method: 'POST', body: formData }) .then(r => r.text()) .then(res => { alert(res); map.closePopup(); if (tabel === 'jalan') loadJalan(); if (tabel === 'parsil') loadParsil(); }) .catch(error => console.error("Error updating data:", error)); } // ========================================== // 4. HAPUS DATA // ========================================== function deleteData(tabel, id) { if (confirm('Apakah Anda yakin ingin menghapus data ini secara permanen?')) { var formData = new FormData(); formData.append('id', id); formData.append('tabel', tabel); fetch('delete_data.php', { method: 'POST', body: formData }) .then(r => r.text()) .then(res => { alert(res); map.closePopup(); if (tabel === 'jalan') loadJalan(); if (tabel === 'parsil') loadParsil(); }) .catch(error => console.error("Error deleting data:", error)); } } // ========================================== // 5. SISTEM PENCARIAN & AUTOCMPLETE // ========================================== function jalankanPencarian() { var kataKunci = document.getElementById('search-input').value.toLowerCase(); var kategori = document.getElementById('search-category').value; var wadahHasil = document.getElementById('search-results'); wadahHasil.innerHTML = ''; if (kataKunci.length < 2) { wadahHasil.style.display = 'none'; return; } var hasil = databasePencarian.filter(function (item) { var cocokNama = item.namaLower.includes(kataKunci); var cocokKategori = (kategori === 'Semua') || (item.kategori === kategori); return cocokNama && cocokKategori; }); if (hasil.length > 0) { wadahHasil.style.display = 'block'; wadahHasil.innerHTML = `
${hasil.length} hasil ditemukan
`; hasil.forEach(function (item) { var badgeClass = item.kategori === 'Jalan' ? 'badge-Jalan' : 'badge-Parsil'; var bgColors = item.kategori === 'Jalan' ? 'rgba(245,158,11,0.1)' : 'rgba(163,116,255,0.1)'; var div = document.createElement('div'); div.className = 'result-item'; div.innerHTML = `
${item.ikon}
${item.namaAsli}
Klik untuk zoom ke lokasi
${item.kategori}`; div.onclick = function () { map.fitBounds(item.layer.getBounds(), { padding: [50, 50] }); item.layer.openPopup(); wadahHasil.style.display = 'none'; document.getElementById('search-input').value = item.namaAsli; toggleClearBtn(); }; wadahHasil.appendChild(div); }); } else { wadahHasil.style.display = 'block'; wadahHasil.innerHTML = `
Data tidak ditemukan
`; } } function toggleClearBtn() { var input = document.getElementById('search-input'); var clearBtn = document.getElementById('search-clear'); clearBtn.style.display = input.value.length > 0 ? 'flex' : 'none'; } function clearSearch() { document.getElementById('search-input').value = ''; document.getElementById('search-results').style.display = 'none'; document.getElementById('search-clear').style.display = 'none'; } // Tutup hasil saat klik di luar document.addEventListener('click', function (e) { var wrapper = document.querySelector('.search-wrapper'); if (wrapper && !wrapper.contains(e.target)) { document.getElementById('search-results').style.display = 'none'; } }); // Shortcut Ctrl + K untuk fokus ke pencarian document.addEventListener('keydown', function (e) { if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault(); document.getElementById('search-input').focus(); } if (e.key === 'Escape') { clearSearch(); document.getElementById('search-input').blur(); } }); // ========================================== // 6. LOGIKA INPUT SIDEBAR & PROGRAMMATIC DRAW // ========================================== // Buka/Tutup Sidebar document.getElementById('sidebar-toggle').addEventListener('click', function () { var sidebar = document.getElementById('input-sidebar'); sidebar.classList.toggle('open'); this.classList.toggle('open'); }); document.getElementById('sidebar-close').addEventListener('click', function () { document.getElementById('input-sidebar').classList.remove('open'); document.getElementById('sidebar-toggle').classList.remove('open'); }); // Ganti Tab function switchSidebarTab(tabName) { document.querySelectorAll('.stab').forEach(function (t) { t.classList.remove('active'); }); document.querySelectorAll('.stab-content').forEach(function (c) { c.classList.remove('active'); }); document.querySelector('.stab[data-tab="' + tabName + '"]').classList.add('active'); document.getElementById('tab-' + tabName).classList.add('active'); // Batalkan mode draw jika ganti tab deactivatePickMode(); } // Aktifkan mode gambar garis/poligon secara terprogram function activateDrawMode(type) { deactivatePickMode(); sidebarDrawMode = type; if (type === 'polyline') { sidebarDrawHandler = new L.Draw.Polyline(map, { shapeOptions: { color: '#6366f1', weight: 4 } }); document.getElementById('coords-jalan-val').textContent = 'Sedang menggambar...'; document.getElementById('btn-draw-jalan').classList.add('active-pick'); } else if (type === 'polygon') { sidebarDrawHandler = new L.Draw.Polygon(map, { shapeOptions: { color: '#6366f1', fillColor: '#6366f1', fillOpacity: 0.3 } }); document.getElementById('coords-parsil-val').textContent = 'Sedang menggambar...'; document.getElementById('btn-draw-parsil').classList.add('active-pick'); } if (sidebarDrawHandler) sidebarDrawHandler.enable(); document.getElementById('pick-overlay').classList.add('active'); var teks = { polyline: 'Klik titik-titik ruas jalan di peta. Klik dua kali titik terakhir untuk selesai.', polygon: 'Klik titik-titik batas lahan di peta. Klik dua kali titik terakhir untuk selesai.' }; document.getElementById('pick-overlay-text').textContent = teks[type]; map.getContainer().style.cursor = 'crosshair'; } // Batalkan mode draw function deactivatePickMode() { sidebarDrawMode = null; document.getElementById('pick-overlay').classList.remove('active'); document.querySelectorAll('.btn-pick-location').forEach(function (b) { b.classList.remove('active-pick'); }); map.getContainer().style.cursor = ''; if (sidebarDrawHandler) { try { sidebarDrawHandler.disable(); } catch (e) { } sidebarDrawHandler = null; } } // Menangkap event Leaflet Draw CREATED map.on(L.Draw.Event.CREATED, function (e) { var type = e.layerType, layer = e.layer; var geojson = layer.toGeoJSON(); drawnItems.clearLayers(); drawnItems.addLayer(layer); if (sidebarDrawMode === 'polyline' && type === 'polyline') { // turf.length menghitung dalam KM, ubah ke meter var panjangMeter = (turf.length(geojson, { units: 'kilometers' }) * 1000).toFixed(2); sidebarKoordinat = JSON.stringify(geojson.geometry); var el = document.getElementById('coords-jalan-val'); el.textContent = panjangMeter + ' m'; el.classList.add('has-coords'); document.getElementById('sb_panjang_jalan').value = panjangMeter + ' m'; deactivatePickMode(); } else if (sidebarDrawMode === 'polygon' && type === 'polygon') { // turf.area menghitung dalam meter persegi var luasMeter = turf.area(geojson).toFixed(2); sidebarKoordinat = JSON.stringify(geojson.geometry); var el = document.getElementById('coords-parsil-val'); el.textContent = luasMeter + ' m²'; el.classList.add('has-coords'); document.getElementById('sb_luas_parsil').value = luasMeter + ' m²'; deactivatePickMode(); } }); // ========================================== // 7. SIMPAN DATA DARI SIDEBAR // ========================================== function saveJalanSidebar() { if (!sidebarKoordinat) { alert('Silakan gambar ruas jalan di peta terlebih dahulu!'); return; } var nama = document.getElementById('sb_nama_jalan').value.trim(); if (!nama) { alert('Nama jalan wajib diisi!'); return; } var formData = new FormData(); formData.append('nama_jalan', nama); formData.append('status', document.getElementById('sb_status_jalan').value); formData.append('panjang', document.getElementById('sb_panjang_jalan').value); formData.append('koordinat', sidebarKoordinat); fetch('save_jalan.php', { method: 'POST', body: formData }) .then(r => r.text()) .then(res => { alert(res); document.getElementById('sb_nama_jalan').value = ''; document.getElementById('sb_panjang_jalan').value = ''; document.getElementById('coords-jalan-val').textContent = 'Belum digambar'; document.getElementById('coords-jalan-val').classList.remove('has-coords'); sidebarKoordinat = ''; drawnItems.clearLayers(); loadJalan(); }) .catch(e => console.error("Error saving jalan:", e)); } function saveParsilSidebar() { if (!sidebarKoordinat) { alert('Silakan gambar area parsil di peta terlebih dahulu!'); return; } var nama = document.getElementById('sb_nama_parsil').value.trim(); if (!nama) { alert('Nama pemilik wajib diisi!'); return; } var formData = new FormData(); formData.append('nama_pemilik', nama); formData.append('status', document.getElementById('sb_status_parsil').value); formData.append('luas', document.getElementById('sb_luas_parsil').value); formData.append('koordinat', sidebarKoordinat); fetch('save_parsil.php', { method: 'POST', body: formData }) .then(r => r.text()) .then(res => { alert(res); document.getElementById('sb_nama_parsil').value = ''; document.getElementById('sb_luas_parsil').value = ''; document.getElementById('coords-parsil-val').textContent = 'Belum digambar'; document.getElementById('coords-parsil-val').classList.remove('has-coords'); sidebarKoordinat = ''; drawnItems.clearLayers(); loadParsil(); }) .catch(e => console.error("Error saving parsil:", e)); } // ========================================== // 8. DROPDOWN KATEGORI DI HEADER // ========================================== document.addEventListener('DOMContentLoaded', function () { var wrap = document.getElementById('custom-select-wrap'); var trigger = document.getElementById('custom-select-trigger'); var options = document.getElementById('custom-select-options'); var hiddenInput = document.getElementById('search-category'); var optionItems = document.querySelectorAll('.custom-option'); // Toggle dropdown wrap.addEventListener('click', function (e) { e.stopPropagation(); options.classList.toggle('show'); }); // Pilihan dropdown diklik optionItems.forEach(function (item) { item.addEventListener('click', function (e) { e.stopPropagation(); optionItems.forEach(opt => opt.classList.remove('selected')); this.classList.add('selected'); trigger.textContent = this.textContent; hiddenInput.value = this.getAttribute('data-value'); options.classList.remove('show'); jalankanPencarian(); }); }); // Tutup dropdown jika klik di luar document.addEventListener('click', function () { options.classList.remove('show'); }); });