/* ========================================================= skpl-layers.js β€” Layer SKPL reusable untuk peta login. Choropleth kelurahan (KF-06) + Heatmap (KF-08) + legenda + layer switcher overlay (KF-09) + pencarian wilayah (KF-11). Pakai: panggil attachSkplLayers(map) setelah map dibuat. Butuh leaflet.heat sudah dimuat di halaman. ========================================================= */ (function () { function injectStyle() { if (document.getElementById('skpl-layer-style')) return; const css = ` .skpl-legend{background:#fff;padding:10px 12px;border-radius:10px;box-shadow:0 2px 10px rgba(0,0,0,.15);font-size:.76rem;line-height:1.6} .skpl-legend h4{font-size:.78rem;margin:0 0 6px} .skpl-legend i{display:inline-block;width:13px;height:13px;border-radius:3px;margin-right:6px;vertical-align:middle} .skpl-search{background:#fff;border-radius:10px;box-shadow:0 2px 10px rgba(0,0,0,.15);padding:6px;width:220px} .skpl-search input{width:100%;border:1px solid #E5E7EB;border-radius:7px;padding:7px 9px;font-size:.83rem;outline:none} .skpl-search input:focus{border-color:#2563EB} .skpl-results .item{padding:7px 9px;font-size:.82rem;cursor:pointer;border-radius:6px} .skpl-results .item:hover{background:#EFF6FF;color:#1D4ED8}`; const s = document.createElement('style'); s.id = 'skpl-layer-style'; s.textContent = css; document.head.appendChild(s); } function colorFor(p) { return p >= 10 ? '#EF4444' : p >= 5 ? '#FB923C' : p >= 2 ? '#FBBF24' : p > 0 ? '#A3E635' : '#2ECC71'; } window.attachSkplLayers = async function (map) { if (!map || typeof L === 'undefined') return; injectStyle(); const API = (typeof APP_BASE !== 'undefined' ? APP_BASE : '') + '/api'; let features = []; // Choropleth let choro = null; try { const j = await (await fetch(`${API}/wilayah.php`)).json(); const data = j.status === 'success' ? j.data : { type: 'FeatureCollection', features: [] }; features = data.features || []; choro = L.geoJSON(data, { style: f => ({ fillColor: colorFor(f.properties.persentase_miskin || 0), fillOpacity: 0.5, color: '#374151', weight: 1.5, }), onEachFeature: (f, layer) => { const p = f.properties; layer.bindPopup(`
${p.nama}
Penduduk: ${p.jumlah_penduduk}
Warga miskin: ${p.jumlah_miskin}
Persentase: ${p.persentase_miskin}%
`); layer.bindTooltip(p.nama, { sticky: true }); }, }); } catch (e) { /* abaikan */ } // Heatmap let heat = null; try { const j = await (await fetch(`${API}/heatmap.php`)).json(); const pts = j.status === 'success' ? j.data : []; if (L.heatLayer) { heat = L.heatLayer(pts, { radius: 30, blur: 22, maxZoom: 17, gradient: { 0.2: '#3B82F6', 0.5: '#FBBF24', 0.9: '#EF4444' }, }); } } catch (e) { /* abaikan */ } // Layer switcher overlay const overlays = {}; if (choro) overlays['πŸ—ΊοΈ Choropleth Kelurahan'] = choro; if (heat) overlays['πŸ”₯ Heatmap Kepadatan'] = heat; if (Object.keys(overlays).length) { L.control.layers(null, overlays, { collapsed: true, position: 'topright' }).addTo(map); } // Legenda const legend = L.control({ position: 'bottomleft' }); legend.onAdd = function () { const div = L.DomUtil.create('div', 'skpl-legend'); const g = [ ['#2ECC71', 'Tidak ada kasus'], ['#A3E635', '< 2%'], ['#FBBF24', '2 – 5%'], ['#FB923C', '5 – 10%'], ['#EF4444', '> 10% (prioritas)'], ]; div.innerHTML = '

Tingkat Kemiskinan

' + g.map(x => `
${x[1]}
`).join(''); return div; }; legend.addTo(map); // Pencarian wilayah (KF-11) const search = L.control({ position: 'topright' }); search.onAdd = function () { const div = L.DomUtil.create('div', 'skpl-search'); div.innerHTML = `
`; L.DomEvent.disableClickPropagation(div); return div; }; search.addTo(map); const root = document.querySelector('.skpl-search'); if (root) { const input = root.querySelector('input'); const res = root.querySelector('.skpl-results'); input.addEventListener('input', function () { const q = this.value.toLowerCase().trim(); res.innerHTML = ''; if (!q) return; features.filter(f => f.properties.nama.toLowerCase().includes(q)).slice(0, 6).forEach(f => { const it = document.createElement('div'); it.className = 'item'; it.textContent = f.properties.nama; it.onclick = () => { map.fitBounds(L.geoJSON(f).getBounds(), { padding: [60, 60] }); res.innerHTML = ''; input.value = f.properties.nama; }; res.appendChild(it); }); }); } }; })();