// ============================================================ // feature-basemap.js // Layer referensi statis dari GeoJSON lokal: // - Batas Kecamatan Kota Pontianak (Admin_Kecamatan) // - Jaringan Jalan (Jaringan_Jalan) // - Sungai Besar (Sungai_Besar) // ============================================================ const basemapCfg = { kecamatan: { label: 'Batas Kecamatan', file: 'assets/map/Admin_Kecamatan.geojson', color: '#7c3aed', fillColor: '#7c3aed', fillOpacity: 0.08, weight: 2.5, icon: '🏙️', popupProp: ['KECAMATAN', 'KELURAHAN', 'KODE_KEC', 'LUAS_HA'], popupLabel: { KECAMATAN: 'Kecamatan', KELURAHAN: 'Kelurahan', KODE_KEC: 'Kode', LUAS_HA: 'Luas (ha)' } }, jalan: { label: 'Jaringan Jalan', file: 'assets/map/Jaringan_Jalan.geojson', color: '#d97706', weight: 1.8, fillOpacity: 0, icon: '🛤️', popupProp: ['NAMOBJ', 'REMARK', 'FCODE'], popupLabel: { NAMOBJ: 'Nama', REMARK: 'Keterangan', FCODE: 'Kode' } }, sungai: { label: 'Sungai Besar', file: 'assets/map/Sungai_Besar.geojson', color: '#0ea5e9', fillColor: '#0ea5e9', fillOpacity: 0.25, weight: 1.5, icon: '🌊', popupProp: ['NAMOBJ', 'REMARK', 'FCODE'], popupLabel: { NAMOBJ: 'Nama', REMARK: 'Keterangan', FCODE: 'Kode' } } }; // Cache untuk data GeoJSON yang sudah di-fetch agar tidak fetch ulang const _basemapCache = {}; /** * Fetch GeoJSON dan buat Leaflet GeoJSON layer dengan style + popup. */ async function loadBasemapLayer(key) { const cfg = basemapCfg[key]; const layerKey = 'basemap_' + key; // Jika layer sudah ada isinya, tidak perlu reload let hasLayers = false; layers[layerKey].eachLayer(() => { hasLayers = true; }); if (hasLayers) return; try { // Gunakan cache jika sudah pernah di-fetch let geojsonData = _basemapCache[key]; if (!geojsonData) { const resp = await fetch(cfg.file); if (!resp.ok) throw new Error(`HTTP ${resp.status}`); geojsonData = await resp.json(); _basemapCache[key] = geojsonData; } const styleOpts = { color: cfg.color, weight: cfg.weight, fillOpacity: cfg.fillOpacity ?? 0, fillColor: cfg.fillColor || cfg.color, opacity: 0.9 }; const geoLayer = L.geoJSON(geojsonData, { style: styleOpts, pointToLayer: (feature, latlng) => L.circleMarker(latlng, { radius: 5, ...styleOpts }), onEachFeature: (feature, layer) => { const props = feature.properties || {}; // Cari nama property (case-insensitive fallback) const findVal = (keys) => { for (const k of keys) { const found = Object.keys(props).find(p => p.toUpperCase() === k.toUpperCase()); if (found && props[found]) return props[found]; } return null; }; const rows = cfg.popupProp .map(p => { const found = Object.keys(props).find(k => k.toUpperCase() === p.toUpperCase()); const val = found ? props[found] : null; if (!val) return ''; return `

${cfg.popupLabel[p] || p}: ${val}

`; }) .filter(Boolean) .join(''); const nameKeys = ['NAMOBJ', 'NAMA', 'NAME', 'KECAMATAN', 'KELURAHAN']; const title = findVal(nameKeys) || cfg.label; if (rows) { layer.bindPopup(`

${cfg.icon} ${esc(title)}

${rows}`, { maxWidth: 280 }); } } }); layers[layerKey].addLayer(geoLayer); } catch (err) { console.warn(`[basemap] Gagal memuat ${cfg.file}:`, err); toast(`Gagal memuat layer ${cfg.label}`, 'error'); } } /** * Render panel kontrol untuk layer basemap di sidebar. * Dipanggil dari renderPanel() di app-core.js jika tab === 'basemap'. */ async function renderBasemapPanel(pb) { pb.innerHTML = `
Layer Referensi Kota Pontianak
${Object.entries(basemapCfg).map(([key, cfg]) => { const lk = 'basemap_' + key; const checked = layerVisible[lk]; return `
${checked ? '✓' : ''}
${cfg.icon} ${cfg.label}
`; }).join('')}
Informasi
Data batas administrasi kecamatan, jaringan jalan, dan sungai besar Kota Pontianak.
Klik objek di peta untuk melihat informasi detail.
Centang/hilang centang untuk tampil/sembunyikan layer.
`; // Load semua layer yang aktif const loadPromises = Object.keys(basemapCfg) .filter(key => layerVisible['basemap_' + key]) .map(key => loadBasemapLayer(key)); await Promise.all(loadPromises); } /** * Toggle visibilitas layer basemap dan load data jika belum ada. */ window.toggleBasemapLayer = async function(key, el) { const lk = 'basemap_' + key; layerVisible[lk] = !layerVisible[lk]; el.classList.toggle('checked', layerVisible[lk]); el.querySelector('.layer-check').textContent = layerVisible[lk] ? '✓' : ''; if (layerVisible[lk]) { await loadBasemapLayer(key); map.addLayer(layers[lk]); } else { map.removeLayer(layers[lk]); } };