First commit / commit pertama

This commit is contained in:
Mr.Haruna
2026-06-13 11:24:58 +07:00
commit 522c4f7200
166 changed files with 13326 additions and 0 deletions
+141
View File
@@ -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();
});
})();