diff --git a/basemap.html b/basemap.html new file mode 100644 index 0000000..d2db26e --- /dev/null +++ b/basemap.html @@ -0,0 +1,83 @@ + + +
+ + +Pemetaan bidang tanah bersertifikat lengkap dengan informasi status kepemilikan.
+ + + +Batas kecamatan, jaringan jalan, dan sungai besar Kota Pontianak dari data GeoJSON.
+ diff --git a/jalan.html b/jalan.html index 0ded2fc..3c9837b 100644 --- a/jalan.html +++ b/jalan.html @@ -81,6 +81,7 @@ + diff --git a/js/app-core.js b/js/app-core.js index 06d6e44..5823f67 100644 --- a/js/app-core.js +++ b/js/app-core.js @@ -35,14 +35,20 @@ const layers = { persilSHM: L.layerGroup(), persilHGB: L.layerGroup(), persilHGU: L.layerGroup(), - persilHP: L.layerGroup() + persilHP: L.layerGroup(), + // Layer referensi GeoJSON statis + basemap_kecamatan: L.layerGroup(), + basemap_jalan: L.layerGroup(), + basemap_sungai: L.layerGroup() }; const layerVisible = { rumahIbadah: true, ibadahRadius: true, pendudukMiskin: true, spbu24: true, spbuNon24: true, jalanNasional: true, jalanProvinsi: true, jalanKabupaten: true, - persilSHM: true, persilHGB: true, persilHGU: true, persilHP: true + persilSHM: true, persilHGB: true, persilHGU: true, persilHP: true, + // Basemap GeoJSON (default tampil semua) + basemap_kecamatan: true, basemap_jalan: true, basemap_sungai: true }; const searchEntries = []; @@ -320,7 +326,8 @@ function initPage(tab) { kemiskinan: ['rumahIbadah', 'ibadahRadius', 'pendudukMiskin'], spbu: ['spbu24', 'spbuNon24'], jalan: ['jalanNasional', 'jalanProvinsi', 'jalanKabupaten'], - persil: ['persilSHM', 'persilHGB', 'persilHGU', 'persilHP'] + persil: ['persilSHM', 'persilHGB', 'persilHGU', 'persilHP'], + basemap: ['basemap_kecamatan', 'basemap_jalan', 'basemap_sungai'] }; if (tabLayers[tab]) { @@ -329,8 +336,14 @@ function initPage(tab) { }); } - const titles = { kemiskinan: 'Ibadah & Kemiskinan', spbu: 'SPBU', jalan: 'Data Jalan', persil: 'Persil Tanah' }; - document.getElementById('panel-title').textContent = titles[tab]; + const titles = { + kemiskinan: 'Ibadah & Kemiskinan', + spbu: 'SPBU', + jalan: 'Data Jalan', + persil: 'Persil Tanah', + basemap: 'Peta Referensi Pontianak' + }; + document.getElementById('panel-title').textContent = titles[tab] || tab; renderPanel(tab); } @@ -351,6 +364,7 @@ async function renderPanel(tab) { if (tab === 'spbu') await renderSpbuPanel(pb); if (tab === 'jalan') await renderJalanPanel(pb); if (tab === 'persil') await renderPersilPanel(pb); + if (tab === 'basemap') await renderBasemapPanel(pb); } function findGeomLayer(type, id) { diff --git a/js/feature-basemap.js b/js/feature-basemap.js new file mode 100644 index 0000000..041137e --- /dev/null +++ b/js/feature-basemap.js @@ -0,0 +1,168 @@ +// ============================================================ +// 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(`