Files
Webgis-Class/js/feature-basemap.js
T
2026-06-11 17:32:06 +07:00

169 lines
5.7 KiB
JavaScript

// ============================================================
// 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 `<p><b>${cfg.popupLabel[p] || p}:</b> ${val}</p>`;
})
.filter(Boolean)
.join('');
const nameKeys = ['NAMOBJ', 'NAMA', 'NAME', 'KECAMATAN', 'KELURAHAN'];
const title = findVal(nameKeys) || cfg.label;
if (rows) {
layer.bindPopup(`<h4>${cfg.icon} ${esc(title)}</h4>${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 = `
<div class="layer-section">
<div class="layer-section-title">Layer Referensi Kota Pontianak</div>
${Object.entries(basemapCfg).map(([key, cfg]) => {
const lk = 'basemap_' + key;
const checked = layerVisible[lk];
return `
<div class="layer-item ${checked ? 'checked' : ''}" id="basemap-item-${key}" onclick="toggleBasemapLayer('${key}', this)">
<div class="layer-check">${checked ? '✓' : ''}</div>
<div class="layer-dot" style="background:${cfg.color};${cfg.fillOpacity > 0 ? '' : 'border-radius:2px;height:4px;width:18px;border-radius:2px'}"></div>
<div class="layer-label">${cfg.icon} ${cfg.label}</div>
</div>`;
}).join('')}
</div>
<div class="layer-section">
<div class="layer-section-title">Informasi</div>
<div style="font-size:11px;line-height:1.8;color:var(--text2);padding:10px 12px;background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius)">
Data batas administrasi kecamatan, jaringan jalan, dan sungai besar Kota Pontianak.<br>
Klik objek di peta untuk melihat informasi detail.<br>
Centang/hilang centang untuk tampil/sembunyikan layer.
</div>
</div>`;
// 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]);
}
};