First commit / commit pertama
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
/* =========================================================
|
||||
public-map.js — Peta Publik SKPL
|
||||
Choropleth (KF-06) + Heatmap (KF-08) + Layer switcher (KF-09)
|
||||
+ Legenda + Pencarian wilayah (KF-11).
|
||||
`map` & `APP_BASE` disediakan global oleh peta_publik.php.
|
||||
========================================================= */
|
||||
(function () {
|
||||
const API = APP_BASE + '/api';
|
||||
|
||||
let choroplethLayer = null;
|
||||
let heatLayer = null;
|
||||
let wilayahFeatures = [];
|
||||
|
||||
// ---- Skala warna persentase kemiskinan ----
|
||||
function colorFor(persen) {
|
||||
return persen >= 10 ? '#EF4444' // merah — tinggi
|
||||
: persen >= 5 ? '#FB923C' // oranye — sedang-tinggi
|
||||
: persen >= 2 ? '#FBBF24' // kuning — sedang
|
||||
: persen > 0 ? '#A3E635' // hijau muda — rendah
|
||||
: '#2ECC71'; // hijau — tanpa kasus
|
||||
}
|
||||
|
||||
function styleWilayah(feature) {
|
||||
const p = feature.properties.persentase_miskin || 0;
|
||||
return {
|
||||
fillColor: colorFor(p),
|
||||
fillOpacity: 0.55,
|
||||
color: '#374151',
|
||||
weight: 1.5,
|
||||
};
|
||||
}
|
||||
|
||||
function popupWilayah(props) {
|
||||
return `<div style="font-weight:700;margin-bottom:6px;">${props.nama}</div>
|
||||
<div style="font-size:0.85rem;line-height:1.6;">
|
||||
Penduduk: <strong>${props.jumlah_penduduk}</strong><br>
|
||||
Warga miskin: <strong>${props.jumlah_miskin}</strong><br>
|
||||
Persentase: <strong>${props.persentase_miskin}%</strong>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// ---- Choropleth (KF-06) ----
|
||||
async function loadChoropleth() {
|
||||
const r = await fetch(`${API}/wilayah.php`);
|
||||
const j = await r.json();
|
||||
const data = j.status === 'success' ? j.data : { type: 'FeatureCollection', features: [] };
|
||||
wilayahFeatures = data.features || [];
|
||||
|
||||
choroplethLayer = L.geoJSON(data, {
|
||||
style: styleWilayah,
|
||||
onEachFeature: (f, layer) => {
|
||||
layer.bindPopup(popupWilayah(f.properties));
|
||||
layer.bindTooltip(f.properties.nama, { sticky: true });
|
||||
},
|
||||
}).addTo(map);
|
||||
|
||||
if (wilayahFeatures.length) {
|
||||
map.fitBounds(choroplethLayer.getBounds(), { padding: [40, 40] });
|
||||
}
|
||||
return choroplethLayer;
|
||||
}
|
||||
|
||||
// ---- Heatmap (KF-08) ----
|
||||
async function loadHeatmap() {
|
||||
const r = await fetch(`${API}/heatmap.php`);
|
||||
const j = await r.json();
|
||||
const points = j.status === 'success' ? j.data : [];
|
||||
heatLayer = L.heatLayer(points, {
|
||||
radius: 30, blur: 22, maxZoom: 17,
|
||||
gradient: { 0.2: '#3B82F6', 0.5: '#FBBF24', 0.9: '#EF4444' },
|
||||
});
|
||||
return heatLayer;
|
||||
}
|
||||
|
||||
// ---- Legenda (KF-06) ----
|
||||
function addLegend() {
|
||||
const legend = L.control({ position: 'bottomleft' });
|
||||
legend.onAdd = function () {
|
||||
const div = L.DomUtil.create('div', 'legend');
|
||||
const grades = [
|
||||
{ c: '#2ECC71', t: 'Tidak ada kasus' },
|
||||
{ c: '#A3E635', t: '< 2% miskin' },
|
||||
{ c: '#FBBF24', t: '2 – 5%' },
|
||||
{ c: '#FB923C', t: '5 – 10%' },
|
||||
{ c: '#EF4444', t: '> 10% (prioritas)' },
|
||||
];
|
||||
div.innerHTML = '<h4>Tingkat Kemiskinan</h4>' +
|
||||
grades.map(g => `<div><i style="background:${g.c}"></i>${g.t}</div>`).join('');
|
||||
return div;
|
||||
};
|
||||
legend.addTo(map);
|
||||
}
|
||||
|
||||
// ---- Pencarian wilayah (KF-11) ----
|
||||
function addSearch() {
|
||||
const ctrl = L.control({ position: 'topright' });
|
||||
ctrl.onAdd = function () {
|
||||
const div = L.DomUtil.create('div', 'map-search');
|
||||
div.innerHTML = `<input type="text" id="searchWilayah" placeholder="🔍 Cari kelurahan..." autocomplete="off">
|
||||
<div id="searchResults" class="search-results"></div>`;
|
||||
L.DomEvent.disableClickPropagation(div);
|
||||
return div;
|
||||
};
|
||||
ctrl.addTo(map);
|
||||
|
||||
const input = document.getElementById('searchWilayah');
|
||||
const results = document.getElementById('searchResults');
|
||||
|
||||
input.addEventListener('input', function () {
|
||||
const q = this.value.toLowerCase().trim();
|
||||
results.innerHTML = '';
|
||||
if (!q) return;
|
||||
wilayahFeatures
|
||||
.filter(f => f.properties.nama.toLowerCase().includes(q))
|
||||
.slice(0, 6)
|
||||
.forEach(f => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'search-item';
|
||||
item.textContent = f.properties.nama;
|
||||
item.onclick = () => {
|
||||
const layer = L.geoJSON(f);
|
||||
map.fitBounds(layer.getBounds(), { padding: [60, 60] });
|
||||
results.innerHTML = '';
|
||||
input.value = f.properties.nama;
|
||||
};
|
||||
results.appendChild(item);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ---- Inisialisasi ----
|
||||
Promise.all([loadChoropleth(), loadHeatmap()]).then(([choro, heat]) => {
|
||||
// Layer switcher (KF-09)
|
||||
L.control.layers(null, {
|
||||
'Batas Kelurahan (Choropleth)': choro,
|
||||
'Heatmap Kepadatan': heat,
|
||||
}, { collapsed: false, position: 'topright' }).addTo(map);
|
||||
addLegend();
|
||||
addSearch();
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user