879 lines
39 KiB
JavaScript
879 lines
39 KiB
JavaScript
// ==========================================
|
|
// 1. INISIALISASI PETA & LAYER
|
|
// ==========================================
|
|
// 1. Definisikan Pilihan Basemap (Tile Layers)
|
|
var osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
|
});
|
|
|
|
var googleSatellite = L.tileLayer('http://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', {
|
|
maxZoom: 20,
|
|
subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
|
|
attribution: 'Map data © Google'
|
|
});
|
|
|
|
var darkMap = L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
|
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>'
|
|
});
|
|
|
|
// 2. Inisialisasi Peta
|
|
// Pastikan id 'map' sesuai dengan id div pada file index.php Anda
|
|
var map = L.map('map', {
|
|
center: [-0.0227, 109.3340], // Titik tengah peta (Pontianak)
|
|
zoom: 13,
|
|
layers: [osm] // Basemap default yang langsung aktif saat web dibuka
|
|
});
|
|
|
|
// 3. Gabungkan Basemap ke dalam satu Grup Objek
|
|
var baseMaps = {
|
|
"Peta Standar (OSM)": osm,
|
|
"Satelit (Google)": googleSatellite,
|
|
"Mode Gelap (Dark)": darkMap
|
|
};
|
|
|
|
// 4. Tambahkan Tombol Kontrol Layer ke Kanan Atas Peta
|
|
L.control.layers(baseMaps).addTo(map);
|
|
|
|
// Zoom control di kanan bawah (lebih bersih)
|
|
L.control.zoom({ position: 'bottomright' }).addTo(map);
|
|
|
|
// Custom pane initialization removed
|
|
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
|
}).addTo(map);
|
|
|
|
// Layer Group untuk masing-masing data
|
|
var drawnItems = new L.FeatureGroup().addTo(map);
|
|
//var batasKotaLayer = L.layerGroup().addTo(map);
|
|
//var sungaiLayer = L.layerGroup().addTo(map);
|
|
var rumahIbadahLayer = L.layerGroup().addTo(map);
|
|
var radiusLayer = L.layerGroup().addTo(map);
|
|
var pendudukMiskinLayer = L.layerGroup().addTo(map);
|
|
var pendudukMiskinCluster = L.markerClusterGroup({
|
|
showCoverageOnHover: false,
|
|
zoomToBoundsOnClick: true,
|
|
maxClusterRadius: 50
|
|
}).addTo(map);
|
|
|
|
// Inisialisasi Heatmap kosong (akan diisi datanya saat load data)
|
|
var povertyHeatLayer = L.heatLayer([], { radius: 22, blur: 18, maxZoom: 17 }).addTo(map);
|
|
|
|
// Inisialisasi Layers Control (di kanan atas)
|
|
var overlayMaps = {
|
|
"🕌 Rumah Ibadah": rumahIbadahLayer,
|
|
"🔥 Kepadatan Kemiskinan (Heatmap)": povertyHeatLayer,
|
|
"👥 Semua Penduduk Miskin": pendudukMiskinCluster,
|
|
"👥 Penduduk Terdekat (Analisis)": pendudukMiskinLayer
|
|
};
|
|
L.control.layers(null, overlayMaps, { collapsed: true, position: 'bottomleft' }).addTo(map);
|
|
|
|
// ==========================================
|
|
// ICON DEFINITIONS
|
|
// ==========================================
|
|
// Unused marker icons removed
|
|
|
|
// Icon Rumah Ibadah Kustom — bangunan rumah ibadah bergaya universal
|
|
var rumahIbadahIcon = L.divIcon({
|
|
className: '',
|
|
html: `<div style="
|
|
width:38px; height:38px;
|
|
background: linear-gradient(135deg, #1e40af, #3b82f6);
|
|
border-radius: 50% 50% 50% 0;
|
|
transform: rotate(-45deg);
|
|
box-shadow: 0 3px 12px rgba(59,130,246,0.55);
|
|
border: 2.5px solid rgba(255,255,255,0.3);
|
|
display:flex; align-items:center; justify-content:center;
|
|
"><svg style="transform:rotate(45deg); width:20px; height:20px;" fill="white" viewBox="0 0 24 24">
|
|
<path d="M12 2L2 7v1h2v13h16V8h2V7L12 2zm0 2.3L20 8.5V19H4V8.5L12 4.3zm-1 6.7v7h2v-7h-2zm-3 2v5h2v-5H8zm6 0v5h2v-5h-2zM11 8a1 1 0 100 2 1 1 0 000-2z"/>
|
|
</svg></div>`,
|
|
iconSize: [38, 38],
|
|
iconAnchor: [19, 38],
|
|
popupAnchor: [0, -38]
|
|
});
|
|
|
|
var databasePendudukMiskin = [];
|
|
|
|
function loadRumahIbadah() {
|
|
rumahIbadahLayer.clearLayers();
|
|
|
|
fetch('load.php?jenis=rumah_ibadah')
|
|
.then(res => res.json())
|
|
.then(dataRumahIbadah => {
|
|
fetch('load.php?jenis=penduduk_miskin')
|
|
.then(res => res.json())
|
|
.then(dataPendudukMiskin => {
|
|
databasePendudukMiskin = dataPendudukMiskin;
|
|
|
|
// Mengisi koordinat data kemiskinan ke dalam Heatmap
|
|
var heatPoints = dataPendudukMiskin.map(p => [
|
|
parseFloat(p.lat || p.latitude),
|
|
parseFloat(p.lng || p.longitude),
|
|
1.0
|
|
]);
|
|
povertyHeatLayer.setLatLngs(heatPoints);
|
|
|
|
// Marker Clustering untuk semua penduduk miskin
|
|
pendudukMiskinCluster.clearLayers();
|
|
dataPendudukMiskin.forEach(penduduk => {
|
|
var latPenduduk = parseFloat(penduduk.lat || penduduk.latitude);
|
|
var lngPenduduk = parseFloat(penduduk.lng || penduduk.longitude);
|
|
var statusBantuan = penduduk.bantuan || 'Belum Menerima';
|
|
|
|
var poorFamilyIcon = L.divIcon({
|
|
className: '',
|
|
html: `<div style="
|
|
width:14px; height:14px;
|
|
background: ${statusBantuan === 'Sudah Menerima' ? '#10b981' : '#ef4444'};
|
|
border-radius: 50%;
|
|
border: 2px solid white;
|
|
box-shadow: 0 0 6px rgba(0,0,0,0.5);
|
|
"></div>`,
|
|
iconSize: [14, 14],
|
|
iconAnchor: [7, 7],
|
|
popupAnchor: [0, -7]
|
|
});
|
|
|
|
var actionButtons = '';
|
|
if (IS_ADMIN) {
|
|
actionButtons = `
|
|
<div class="btn-group">
|
|
<button class="btn btn-edit" onclick="formEditPenduduk('${penduduk.id}', '${penduduk.nama_kk}', '${penduduk.alamat}', '${statusBantuan}', ${latPenduduk}, ${lngPenduduk})">✏️ Edit</button>
|
|
<button class="btn btn-delete" onclick="deleteData('penduduk_miskin', '${penduduk.id}')">🗑️ Hapus</button>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
var htmlIsi = `
|
|
<div class="popup-container" id="popup-penduduk-${penduduk.id}">
|
|
<div class="popup-header" style="background: linear-gradient(135deg, ${statusBantuan === 'Sudah Menerima' ? '#065f46, #10b981' : '#7f1d1d, #ef4444'});">
|
|
<h4>${penduduk.nama_kk}</h4>
|
|
<span class="popup-type-badge">👥 Data Sosial</span>
|
|
</div>
|
|
<div class="popup-body">
|
|
<div class="info-row">
|
|
<span class="info-label">Alamat Lengkap</span>
|
|
<span class="info-value" style="font-size:11.5px; line-height:1.4;">${penduduk.alamat}</span>
|
|
</div>
|
|
<div class="info-row">
|
|
<span class="info-label">Status Bantuan</span>
|
|
<span class="info-value" style="color: ${statusBantuan === 'Sudah Menerima' ? '#34d399' : '#f87171'}; font-weight: 600;">${statusBantuan}</span>
|
|
</div>
|
|
${actionButtons}
|
|
</div>
|
|
</div>`;
|
|
|
|
var marker = L.marker([latPenduduk, lngPenduduk], { icon: poorFamilyIcon })
|
|
.bindPopup(htmlIsi);
|
|
|
|
pendudukMiskinCluster.addLayer(marker);
|
|
});
|
|
|
|
dataRumahIbadah.forEach(ibadah => {
|
|
var latIbadah = ibadah.lat || ibadah.latitude;
|
|
var lngIbadah = ibadah.lng || ibadah.longitude;
|
|
var radiusMeter = 500;
|
|
var ibadahId = ibadah.id;
|
|
|
|
var actionButtons = '';
|
|
if (IS_ADMIN) {
|
|
actionButtons = `
|
|
<div class="btn-group">
|
|
<button class="btn btn-edit" onclick="formEditRumahIbadah('${ibadah.id}', '${ibadah.nama}', ${latIbadah}, ${lngIbadah})">✏️ Edit</button>
|
|
<button class="btn btn-delete" onclick="deleteData('rumah_ibadah', '${ibadah.id}')">🗑️ Hapus</button>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// HTML untuk Popup Rumah Ibadah dengan range slider untuk mengatur radius analisis
|
|
var htmlIbadah = `
|
|
<div class="popup-container" id="popup-ibadah-${ibadahId}">
|
|
<div class="popup-header" style="background: linear-gradient(135deg, #1e3a5f, #3b82f6);">
|
|
<h4>${ibadah.nama}</h4>
|
|
<span class="popup-type-badge">🕌 Rumah Ibadah</span>
|
|
</div>
|
|
<div class="popup-body">
|
|
<div class="info-row">
|
|
<span class="info-label">Radius Analisis</span>
|
|
<span class="info-value" id="radius-val-${ibadahId}">${radiusMeter} meter</span>
|
|
<input type="range" id="radius-slider-${ibadahId}" min="100" max="2500" step="50" value="${radiusMeter}" oninput="updatePopupRadius('${ibadahId}', ${latIbadah}, ${lngIbadah})" style="width:100%; margin-top:6px; accent-color:#3b82f6; cursor:pointer;">
|
|
</div>
|
|
|
|
${actionButtons}
|
|
|
|
</div>
|
|
</div>`;
|
|
|
|
var ibadahMarker = L.marker([latIbadah, lngIbadah], { icon: rumahIbadahIcon })
|
|
.bindPopup(htmlIbadah)
|
|
.addTo(rumahIbadahLayer);
|
|
|
|
databasePencarian.push({
|
|
namaLower: ibadah.nama.toLowerCase(),
|
|
namaAsli: ibadah.nama,
|
|
kategori: 'Rumah Ibadah',
|
|
ikon: '🕌',
|
|
tipe: 'titik',
|
|
layer: ibadahMarker
|
|
});
|
|
|
|
// Saat popup dibuka, jalankan fungsi updatePopupRadius
|
|
ibadahMarker.on('popupopen', function (e) {
|
|
map.flyTo(e.target.getLatLng(), 16, { animate: true });
|
|
setTimeout(function () {
|
|
updatePopupRadius(ibadahId, latIbadah, lngIbadah);
|
|
}, 50);
|
|
});
|
|
});
|
|
});
|
|
})
|
|
.catch(e => console.log('Belum ada data Rumah Ibadah', e));
|
|
}
|
|
|
|
// Fungsi untuk memperbarui lingkaran radius dan penduduk miskin terdekat secara interaktif
|
|
function updatePopupRadius(ibadahId, lat, lng) {
|
|
var slider = document.getElementById('radius-slider-' + ibadahId);
|
|
var label = document.getElementById('radius-val-' + ibadahId);
|
|
if (!slider || !label) return;
|
|
|
|
var newRadius = parseInt(slider.value);
|
|
label.textContent = newRadius + ' meter';
|
|
|
|
var centerPoint = L.latLng(lat, lng);
|
|
|
|
radiusLayer.clearLayers();
|
|
pendudukMiskinLayer.clearLayers();
|
|
|
|
// Gambar lingkaran radius baru
|
|
var areaRadius = L.circle(centerPoint, {
|
|
color: '#2563eb',
|
|
fillColor: '#3b82f6',
|
|
fillOpacity: 0.1,
|
|
radius: newRadius,
|
|
interactive: true
|
|
}).addTo(radiusLayer);
|
|
|
|
areaRadius.on('click', function (evt) { L.DomEvent.stopPropagation(evt); });
|
|
|
|
var totalKK = 0;
|
|
var sudahBantuan = 0;
|
|
var belumBantuan = 0;
|
|
var familiesInRadius = [];
|
|
|
|
// Saring dan tampilkan penduduk miskin yang berada dalam radius baru
|
|
databasePendudukMiskin.forEach(penduduk => {
|
|
var latPenduduk = penduduk.lat || penduduk.latitude;
|
|
var lngPenduduk = penduduk.lng || penduduk.longitude;
|
|
var pendudukLatLng = L.latLng(latPenduduk, lngPenduduk);
|
|
var jarak = centerPoint.distanceTo(pendudukLatLng);
|
|
|
|
if (jarak <= newRadius) {
|
|
totalKK++;
|
|
var statusBantuan = penduduk.bantuan || 'Belum Menerima';
|
|
if (statusBantuan === 'Sudah Menerima') {
|
|
sudahBantuan++;
|
|
} else {
|
|
belumBantuan++;
|
|
}
|
|
familiesInRadius.push(penduduk);
|
|
|
|
var warnaDot = statusBantuan === 'Sudah Menerima' ? '#10b981' : '#ef4444';
|
|
|
|
var redDot = L.circleMarker(pendudukLatLng, {
|
|
radius: 7,
|
|
color: '#ffffff',
|
|
fillColor: warnaDot,
|
|
fillOpacity: 1,
|
|
weight: 1.5
|
|
}).addTo(pendudukMiskinLayer);
|
|
|
|
redDot.on('click', function (e) {
|
|
L.DomEvent.stopPropagation(e);
|
|
|
|
var pendudukId = penduduk.id;
|
|
var namaKeluarga = penduduk.nama_kk ? penduduk.nama_kk : "Tanpa Nama";
|
|
var alamatLengkap = penduduk.alamat ? penduduk.alamat : "Alamat belum diinput manual";
|
|
var latAngka = parseFloat(latPenduduk);
|
|
var lngAngka = parseFloat(lngPenduduk);
|
|
|
|
var actionButtons = '';
|
|
if (IS_ADMIN) {
|
|
actionButtons = `
|
|
<div class="btn-group">
|
|
<button class="btn btn-edit" onclick="formEditPenduduk('${penduid}', '${namaKeluarga}', '${alamatLengkap}', '${statusBantuan}', ${latAngka}, ${lngAngka})">✏️ Edit</button>
|
|
<button class="btn btn-delete" onclick="deleteData('penduduk_miskin', '${pendudukId}')">🗑️ Hapus</button>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// Typo fix: penduid -> pendudukId
|
|
actionButtons = actionButtons.replace('\'${penduid}\'', `'${pendudukId}'`);
|
|
|
|
var htmlIsi = `
|
|
<div class="popup-container" id="popup-penduduk-${pendudukId}">
|
|
<div class="popup-header" style="background: linear-gradient(135deg, ${statusBantuan === 'Sudah Menerima' ? '#065f46, #10b981' : '#7f1d1d, #ef4444'});">
|
|
<h4>${namaKeluarga}</h4>
|
|
<span class="popup-type-badge">👥 Data Sosial</span>
|
|
</div>
|
|
<div class="popup-body">
|
|
<div class="info-row">
|
|
<span class="info-label">Alamat Lengkap</span>
|
|
<span class="info-value" style="font-size:11.5px; line-height:1.4;">${alamatLengkap}</span>
|
|
</div>
|
|
<div class="info-row">
|
|
<span class="info-label">Status Bantuan</span>
|
|
<span class="info-value" style="color: ${statusBantuan === 'Sudah Menerima' ? '#34d399' : '#f87171'}; font-weight: 600;">${statusBantuan}</span>
|
|
</div>
|
|
${actionButtons}
|
|
</div>
|
|
</div>`;
|
|
|
|
redDot.bindPopup(htmlIsi).openPopup();
|
|
});
|
|
}
|
|
});
|
|
|
|
// Cari nama rumah ibadah dari databasePencarian
|
|
var ibadahObj = databasePencarian.find(item => item.layer && item.layer._popup && item.layer._popup.getContent().includes('popup-ibadah-' + ibadahId));
|
|
var ibadahName = ibadahObj ? ibadahObj.namaAsli : "Rumah Ibadah";
|
|
|
|
showDashboard(ibadahName, totalKK, sudahBantuan, belumBantuan, familiesInRadius);
|
|
}
|
|
|
|
map.on('click', function () {
|
|
if (sidebarPickMode) return; // Ditangani oleh listener sidebar
|
|
radiusLayer.clearLayers();
|
|
pendudukMiskinLayer.clearLayers();
|
|
closeDashboard();
|
|
});
|
|
|
|
loadRumahIbadah();
|
|
|
|
|
|
|
|
// ==========================================
|
|
// 7. HAPUS DATA
|
|
// ==========================================
|
|
function deleteData(tabel, id) {
|
|
if (confirm('Apakah Anda yakin ingin menghapus data ini?')) {
|
|
var formData = new FormData();
|
|
formData.append('id', id);
|
|
formData.append('tabel', tabel);
|
|
fetch('delete_data.php', { method: 'POST', body: formData })
|
|
.then(r => r.text()).then(res => {
|
|
alert(res);
|
|
map.closePopup();
|
|
loadRumahIbadah();
|
|
});
|
|
}
|
|
}
|
|
|
|
// ==========================================
|
|
// 8. EDIT DATA
|
|
// ==========================================
|
|
|
|
|
|
function formEditRumahIbadah(id, nama, lat, lng) {
|
|
setTimeout(function () {
|
|
document.getElementById(`popup-ibadah-${id}`).innerHTML = `
|
|
<div class="popup-header" style="background: linear-gradient(135deg, #1e3a5f, #3b82f6); padding:14px 16px 12px; border-radius: 0;">
|
|
<h4 style="margin:0; font-weight:700; font-size:15px; color:#fff;">Edit Rumah Ibadah</h4>
|
|
</div>
|
|
<div class="popup-body" style="padding:14px 16px;">
|
|
<form onsubmit="updateData(event, 'rumah_ibadah', '${id}')">
|
|
<input type="text" id="edit_nama_ibadah" value="${nama}" required>
|
|
<input type="hidden" id="edit_lat_ibadah" value="${lat}">
|
|
<input type="hidden" id="edit_lng_ibadah" value="${lng}">
|
|
<div class="btn-group">
|
|
<button type="submit" class="btn btn-save">💾 Update</button>
|
|
<button type="button" class="btn btn-delete" onclick="loadRumahIbadah(); map.closePopup();">Batal</button>
|
|
</div>
|
|
</form>
|
|
</div>`;
|
|
}, 50);
|
|
}
|
|
|
|
function formEditPenduduk(id, nama, alamat, statusBantuan, lat, lng) {
|
|
setTimeout(function () {
|
|
document.getElementById(`popup-penduduk-${id}`).innerHTML = `
|
|
<div class="popup-header" style="background: linear-gradient(135deg, #7f1d1d, #ef4444); padding:14px 16px 12px; border-radius: 0;">
|
|
<h4 style="margin:0; font-weight:700; font-size:15px; color:#fff;">Edit Penduduk</h4>
|
|
</div>
|
|
<div class="popup-body" style="padding:14px 16px;">
|
|
<form onsubmit="updateData(event, 'penduduk_miskin', '${id}')">
|
|
<span class="info-label">NAMA KEPALA KELUARGA</span>
|
|
<input type="text" style="margin-bottom:12px; width:100%; padding:8px 10px; background-color:#0f172a; border:1px solid rgba(255,255,255,0.2); border-radius:6px; color:#f8fafc;" id="edit_nama_penduduk" value="${nama}" required>
|
|
<span class="info-label">ALAMAT LENGKAP</span>
|
|
<textarea id="edit_alamat_penduduk" style="margin-bottom:12px; width:100%; padding:8px 10px; background-color:#0f172a; border:1px solid rgba(255,255,255,0.2); border-radius:6px; color:#f8fafc;" rows="2" required>${alamat}</textarea>
|
|
|
|
<span class="info-label">STATUS BANTUAN</span>
|
|
<select id="edit_status_bantuan" style="margin-bottom:12px; width:100%; padding:8px 10px; background-color:#0f172a; border:1px solid rgba(255,255,255,0.2); border-radius:6px; color:#f8fafc; font-size:13px; cursor:pointer;">
|
|
<option value="Belum Menerima" style="background-color:#1e293b; color:#f8fafc;" ${statusBantuan === 'Belum Menerima' ? 'selected' : ''}>Belum Menerima</option>
|
|
<option value="Sudah Menerima" style="background-color:#1e293b; color:#f8fafc;" ${statusBantuan === 'Sudah Menerima' ? 'selected' : ''}>Sudah Menerima</option>
|
|
</select>
|
|
|
|
<input type="hidden" id="edit_lat_penduduk" value="${lat}">
|
|
<input type="hidden" id="edit_lng_penduduk" value="${lng}">
|
|
<div class="btn-group">
|
|
<button type="submit" class="btn btn-save">💾 Update</button>
|
|
<button type="button" class="btn btn-delete" onclick="loadRumahIbadah(); map.closePopup();">Batal</button>
|
|
</div>
|
|
</form>
|
|
</div>`;
|
|
}, 50);
|
|
}
|
|
|
|
// ==========================================
|
|
// 9. UPDATE DATA
|
|
// ==========================================
|
|
function updateData(e, tabel, id) {
|
|
e.preventDefault();
|
|
var formData = new FormData();
|
|
formData.append('tabel', tabel);
|
|
formData.append('id', id);
|
|
|
|
if (tabel === 'rumah_ibadah') {
|
|
formData.append('nama', document.getElementById('edit_nama_ibadah').value);
|
|
formData.append('latitude', document.getElementById('edit_lat_ibadah').value);
|
|
formData.append('longitude', document.getElementById('edit_lng_ibadah').value);
|
|
} else if (tabel === 'penduduk_miskin') {
|
|
formData.append('nama', document.getElementById('edit_nama_penduduk').value);
|
|
formData.append('alamat', document.getElementById('edit_alamat_penduduk').value);
|
|
formData.append('bantuan', document.getElementById('edit_status_bantuan').value);
|
|
formData.append('latitude', document.getElementById('edit_lat_penduduk').value);
|
|
formData.append('longitude', document.getElementById('edit_lng_penduduk').value);
|
|
}
|
|
|
|
fetch('update_data.php', { method: 'POST', body: formData })
|
|
.then(r => r.text()).then(res => {
|
|
alert(res);
|
|
map.closePopup();
|
|
if (tabel === 'rumah_ibadah' || tabel === 'penduduk_miskin') {
|
|
loadRumahIbadah();
|
|
}
|
|
});
|
|
}
|
|
|
|
// ==========================================
|
|
// 10. LEGENDA PETA
|
|
// ==========================================
|
|
var legend = L.control({ position: 'bottomleft' });
|
|
legend.onAdd = function (map) {
|
|
var div = L.DomUtil.create('div', 'info legend');
|
|
div.innerHTML = `
|
|
<div class="legend-icon-btn">
|
|
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" width="16" height="16">
|
|
<path stroke-linecap="round" stroke-linejoin="round"
|
|
d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" />
|
|
</svg>
|
|
</div>
|
|
<div class="legend-content">
|
|
<h4>Legenda Peta</h4>
|
|
|
|
<span class="legend-group">Sosial & Keagamaan</span>
|
|
<div class="legend-item">
|
|
<div style="width:14px; height:14px; background: linear-gradient(135deg, #1e40af, #3b82f6); border-radius: 50% 50% 50% 0; transform: rotate(-45deg); display:flex; align-items:center; justify-content:center; margin-right:10px; margin-left:3px; box-shadow: 0 1px 3px rgba(59,130,246,0.5);">
|
|
<svg style="transform:rotate(45deg); width:8px; height:8px;" fill="white" viewBox="0 0 24 24"><path d="M12 2L2 7v1h2v13h16V8h2V7L12 2zm0 2.3L20 8.5V19H4V8.5L12 4.3zm-1 6.7v7h2v-7h-2zm-3 2v5h2v-5H8zm6 0v5h2v-5h-2zM11 8a1 1 0 100 2 1 1 0 000-2z"/></svg>
|
|
</div>
|
|
Rumah Ibadah
|
|
</div>
|
|
<div class="legend-item"><span class="legend-color" style="background:#34d399; border-color:#34d399; border-radius:50%; height:10px; width:10px; margin-left:2px;"></span> Penduduk Miskin (Sudah Menerima)</div>
|
|
<div class="legend-item"><span class="legend-color" style="background:#ef4444; border-color:#dc2626; border-radius:50%; height:10px; width:10px; margin-left:2px;"></span> Penduduk Miskin (Belum Menerima)</div>
|
|
</div>`;
|
|
return div;
|
|
};
|
|
legend.addTo(map);
|
|
|
|
// ==========================================
|
|
// 11. SISTEM PENCARIAN
|
|
// ==========================================
|
|
var databasePencarian = [];
|
|
|
|
var ikonKategori = {
|
|
'Rumah Ibadah': '🕌',
|
|
};
|
|
|
|
var warnaBadge = {
|
|
'Rumah Ibadah': 'badge-Rumah-Ibadah',
|
|
};
|
|
|
|
function jalankanPencarian() {
|
|
var kataKunci = document.getElementById('search-input').value.toLowerCase();
|
|
var kategori = document.getElementById('search-category').value;
|
|
var wadahHasil = document.getElementById('search-results');
|
|
|
|
wadahHasil.innerHTML = '';
|
|
|
|
if (kataKunci.length < 2) {
|
|
wadahHasil.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
var hasil = databasePencarian.filter(function (item) {
|
|
var cocokNama = item.namaLower.includes(kataKunci);
|
|
var cocokKategori = (kategori === 'Semua') || (item.kategori === kategori);
|
|
return cocokNama && cocokKategori;
|
|
});
|
|
|
|
if (hasil.length > 0) {
|
|
wadahHasil.style.display = 'block';
|
|
wadahHasil.innerHTML = `<div class="results-header">${hasil.length} hasil ditemukan</div>`;
|
|
|
|
hasil.forEach(function (item) {
|
|
var badgeClass = warnaBadge[item.kategori] || 'badge-default';
|
|
var ikon = ikonKategori[item.kategori] || '📍';
|
|
var bgColors = {
|
|
'Rumah Ibadah': 'rgba(59,130,246,0.12)'
|
|
};
|
|
|
|
var div = document.createElement('div');
|
|
div.className = 'result-item';
|
|
div.innerHTML = `
|
|
<div class="result-item-icon" style="background: ${bgColors[item.kategori] || 'rgba(148,163,184,0.1)'};">
|
|
${ikon}
|
|
</div>
|
|
<div class="result-item-text">
|
|
<div class="result-item-name">${item.namaAsli}</div>
|
|
<div class="result-item-sub">Klik untuk zoom ke lokasi</div>
|
|
</div>
|
|
<span class="badge-kategori ${badgeClass}">${item.kategori}</span>`;
|
|
|
|
div.onclick = function () {
|
|
if (item.tipe === 'titik') {
|
|
map.flyTo(item.layer.getLatLng(), 17, { animate: true, duration: 1.5 });
|
|
} else {
|
|
map.fitBounds(item.layer.getBounds());
|
|
}
|
|
item.layer.openPopup();
|
|
wadahHasil.style.display = 'none';
|
|
document.getElementById('search-input').value = item.namaAsli;
|
|
toggleClearBtn();
|
|
};
|
|
wadahHasil.appendChild(div);
|
|
});
|
|
} else {
|
|
wadahHasil.style.display = 'block';
|
|
wadahHasil.innerHTML = `
|
|
<div class="result-empty">
|
|
<div class="result-empty-icon">🔍</div>
|
|
<div>Data tidak ditemukan</div>
|
|
<div style="font-size:11px; margin-top:4px; color:#475569;">Coba kata kunci lain</div>
|
|
</div>`;
|
|
}
|
|
}
|
|
|
|
function toggleClearBtn() {
|
|
var input = document.getElementById('search-input');
|
|
var clearBtn = document.getElementById('search-clear');
|
|
clearBtn.style.display = input.value.length > 0 ? 'flex' : 'none';
|
|
}
|
|
|
|
function clearSearch() {
|
|
document.getElementById('search-input').value = '';
|
|
document.getElementById('search-results').style.display = 'none';
|
|
document.getElementById('search-clear').style.display = 'none';
|
|
}
|
|
|
|
// Tutup hasil saat klik di luar
|
|
document.addEventListener('click', function (e) {
|
|
var wrapper = document.querySelector('.search-wrapper');
|
|
if (!wrapper.contains(e.target)) {
|
|
document.getElementById('search-results').style.display = 'none';
|
|
}
|
|
});
|
|
|
|
// Shortcut Cmd/Ctrl + K
|
|
document.addEventListener('keydown', function (e) {
|
|
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
|
e.preventDefault();
|
|
document.getElementById('search-input').focus();
|
|
}
|
|
if (e.key === 'Escape') {
|
|
clearSearch();
|
|
document.getElementById('search-input').blur();
|
|
}
|
|
});
|
|
|
|
|
|
// ==========================================
|
|
// 12. LOGIKA SIDEBAR INPUT TERPUSAT
|
|
// ==========================================
|
|
|
|
var sidebarPickMode = null; // 'ibadah' | 'penduduk'
|
|
var sidebarTempLat = 0, sidebarTempLng = 0;
|
|
|
|
// Buka/Tutup Sidebar
|
|
document.getElementById('sidebar-toggle').addEventListener('click', function () {
|
|
var sidebar = document.getElementById('input-sidebar');
|
|
sidebar.classList.toggle('open');
|
|
this.classList.toggle('open');
|
|
});
|
|
document.getElementById('sidebar-close').addEventListener('click', function () {
|
|
document.getElementById('input-sidebar').classList.remove('open');
|
|
document.getElementById('sidebar-toggle').classList.remove('open');
|
|
});
|
|
|
|
// Ganti Tab
|
|
function switchSidebarTab(tabName) {
|
|
document.querySelectorAll('.stab').forEach(function (t) { t.classList.remove('active'); });
|
|
document.querySelectorAll('.stab-content').forEach(function (c) { c.classList.remove('active'); });
|
|
document.querySelector('.stab[data-tab="' + tabName + '"]').classList.add('active');
|
|
document.getElementById('tab-' + tabName).classList.add('active');
|
|
// batalkan mode pick/draw jika ganti tab
|
|
deactivatePickMode();
|
|
}
|
|
|
|
// Aktifkan mode pilih titik di peta
|
|
function activatePickMode(jenis) {
|
|
sidebarPickMode = jenis;
|
|
document.getElementById('pick-overlay').classList.add('active');
|
|
var teks = { ibadah: 'Klik di peta untuk posisi Rumah Ibadah', penduduk: 'Klik di peta untuk posisi Penduduk Miskin' };
|
|
document.getElementById('pick-overlay-text').textContent = teks[jenis] || 'Klik lokasi di peta...';
|
|
var btnMap = { ibadah: 'btn-pick-ibadah', penduduk: 'btn-pick-penduduk' };
|
|
if (btnMap[jenis]) document.getElementById(btnMap[jenis]).classList.add('active-pick');
|
|
map.getContainer().style.cursor = 'crosshair';
|
|
}
|
|
|
|
// Batalkan semua mode
|
|
function deactivatePickMode() {
|
|
sidebarPickMode = null;
|
|
document.getElementById('pick-overlay').classList.remove('active');
|
|
document.querySelectorAll('.btn-pick-location').forEach(function (b) { b.classList.remove('active-pick'); });
|
|
map.getContainer().style.cursor = '';
|
|
}
|
|
|
|
// Tangkap klik peta untuk mode pick titik
|
|
map.on('click', function (e) {
|
|
if (!sidebarPickMode) return;
|
|
var lat = e.latlng.lat.toFixed(6);
|
|
var lng = e.latlng.lng.toFixed(6);
|
|
sidebarTempLat = e.latlng.lat;
|
|
sidebarTempLng = e.latlng.lng;
|
|
radiusLayer.clearLayers();
|
|
pendudukMiskinLayer.clearLayers();
|
|
|
|
if (sidebarPickMode === 'ibadah') {
|
|
var el = document.getElementById('coords-ibadah-val');
|
|
el.textContent = lat + ', ' + lng;
|
|
el.classList.add('has-coords');
|
|
} else if (sidebarPickMode === 'penduduk') {
|
|
var el = document.getElementById('coords-penduduk-val');
|
|
el.textContent = lat + ', ' + lng;
|
|
el.classList.add('has-coords');
|
|
var alamatInput = document.getElementById('sb_alamat_penduduk');
|
|
alamatInput.value = "Sedang mencari alamat..."; // Indikator loading
|
|
|
|
// Memanggil API Nominatim OpenStreetMap
|
|
fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data && data.display_name) {
|
|
// Isi textarea alamat dengan hasil dari Nominatim
|
|
alamatInput.value = data.display_name;
|
|
} else {
|
|
alamatInput.value = "Alamat tidak ditemukan, silakan isi manual.";
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error("Error Reverse Geocoding:", error);
|
|
alamatInput.value = "";
|
|
});
|
|
}
|
|
deactivatePickMode();
|
|
});
|
|
|
|
// ===== FUNGSI SIMPAN DARI SIDEBAR =====
|
|
|
|
|
|
function saveRumahIbadahSidebar() {
|
|
var coords = document.getElementById('coords-ibadah-val').textContent;
|
|
if (coords === 'Belum dipilih') { alert('Pilih lokasi di peta terlebih dahulu!'); return; }
|
|
var nama = document.getElementById('sb_nama_ibadah').value.trim();
|
|
if (!nama) { alert('Nama rumah ibadah wajib diisi!'); return; }
|
|
var formData = new FormData();
|
|
formData.append('nama', nama);
|
|
formData.append('latitude', sidebarTempLat);
|
|
formData.append('longitude', sidebarTempLng);
|
|
fetch('save_rumah_ibadah.php', { method: 'POST', body: formData })
|
|
.then(r => r.text()).then(res => {
|
|
alert(res);
|
|
document.getElementById('sb_nama_ibadah').value = '';
|
|
document.getElementById('coords-ibadah-val').textContent = 'Belum dipilih';
|
|
document.getElementById('coords-ibadah-val').classList.remove('has-coords');
|
|
sidebarTempLat = 0; sidebarTempLng = 0;
|
|
loadRumahIbadah();
|
|
}).catch(e => console.error(e));
|
|
}
|
|
|
|
function savePendudukSidebar() {
|
|
var coords = document.getElementById('coords-penduduk-val').textContent;
|
|
if (coords === 'Belum dipilih') { alert('Pilih lokasi di peta terlebih dahulu!'); return; }
|
|
|
|
var nama = document.getElementById('sb_nama_penduduk').value.trim();
|
|
var alamat = document.getElementById('sb_alamat_penduduk').value.trim();
|
|
var statusBantuan = document.getElementById('sb_status_bantuan').value;
|
|
|
|
if (!nama) { alert('Nama kepala keluarga wajib diisi!'); return; }
|
|
if (!alamat) { alert('Alamat wajib diisi!'); return; }
|
|
|
|
var formData = new FormData();
|
|
formData.append('nama', nama);
|
|
formData.append('alamat', alamat);
|
|
formData.append('bantuan', statusBantuan);
|
|
formData.append('latitude', sidebarTempLat);
|
|
formData.append('longitude', sidebarTempLng);
|
|
|
|
fetch('save_penduduk_miskin.php', { method: 'POST', body: formData })
|
|
.then(r => r.text()).then(res => {
|
|
alert(res);
|
|
document.getElementById('sb_nama_penduduk').value = '';
|
|
document.getElementById('sb_alamat_penduduk').value = '';
|
|
document.getElementById('sb_status_bantuan').value = 'Belum Menerima';
|
|
document.getElementById('coords-penduduk-val').textContent = 'Belum dipilih';
|
|
document.getElementById('coords-penduduk-val').classList.remove('has-coords');
|
|
sidebarTempLat = 0; sidebarTempLng = 0;
|
|
loadRumahIbadah();
|
|
}).catch(e => console.error(e));
|
|
}
|
|
|
|
// ==========================================
|
|
// 14. LOGIKA CUSTOM DROPDOWN
|
|
// ==========================================
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
var wrap = document.getElementById('custom-select-wrap');
|
|
var trigger = document.getElementById('custom-select-trigger');
|
|
var options = document.getElementById('custom-select-options');
|
|
var hiddenInput = document.getElementById('search-category');
|
|
var optionItems = document.querySelectorAll('.custom-option');
|
|
|
|
// Buka/Tutup dropdown saat kotak di-klik
|
|
wrap.addEventListener('click', function (e) {
|
|
e.stopPropagation();
|
|
options.classList.toggle('show');
|
|
});
|
|
|
|
// Saat salah satu pilihan (seperti SPBU atau Jalan) diklik
|
|
optionItems.forEach(function (item) {
|
|
item.addEventListener('click', function (e) {
|
|
e.stopPropagation();
|
|
|
|
// Hapus status aktif dari yang lain, pasang ke yang di-klik
|
|
optionItems.forEach(opt => opt.classList.remove('selected'));
|
|
this.classList.add('selected');
|
|
|
|
// Ganti teks yang tampil dan simpan nilainya ke input tersembunyi
|
|
trigger.textContent = this.textContent;
|
|
hiddenInput.value = this.getAttribute('data-value');
|
|
|
|
// Tutup dropdown
|
|
options.classList.remove('show');
|
|
|
|
// Otomatis jalankan fungsi pencarian (kode aslimu)
|
|
jalankanPencarian();
|
|
});
|
|
});
|
|
|
|
// Tutup dropdown otomatis jika klik area manapun di luar peta/header
|
|
document.addEventListener('click', function () {
|
|
options.classList.remove('show');
|
|
});
|
|
});
|
|
|
|
// ==========================================
|
|
// 15. DYNAMIC STATS DASHBOARD SIDEBAR (CHART.JS)
|
|
// ==========================================
|
|
var myPovertyChart = null;
|
|
|
|
function showDashboard(ibadahName, totalKK, sudahBantuan, belumBantuan, familiesInRadius) {
|
|
document.getElementById('dashboard-target-name').textContent = ibadahName;
|
|
document.getElementById('stat-total-kk').textContent = totalKK;
|
|
document.getElementById('stat-sudah-bantuan').textContent = sudahBantuan;
|
|
document.getElementById('stat-belum-bantuan').textContent = belumBantuan;
|
|
|
|
// Tampilkan sidebar kanan
|
|
document.getElementById('dashboard-sidebar').classList.add('open');
|
|
|
|
// Buat daftar KK miskin dalam radius
|
|
var listContainer = document.getElementById('dashboard-families-list');
|
|
listContainer.innerHTML = '';
|
|
|
|
if (familiesInRadius.length === 0) {
|
|
listContainer.innerHTML = '<div class="families-empty">Tidak ada KK Miskin dalam radius ini</div>';
|
|
} else {
|
|
familiesInRadius.forEach(f => {
|
|
var item = document.createElement('div');
|
|
item.className = 'family-item';
|
|
|
|
var statusClass = f.bantuan === 'Sudah Menerima' ? 'badge-success' : 'badge-danger';
|
|
var statusText = f.bantuan === 'Sudah Menerima' ? 'Sudah' : 'Belum';
|
|
|
|
item.innerHTML = `
|
|
<div class="family-details">
|
|
<div class="family-name">${f.nama_kk}</div>
|
|
<div class="family-address">${f.alamat || 'Alamat tidak diisi'}</div>
|
|
</div>
|
|
<span class="family-badge ${statusClass}">${statusText}</span>
|
|
`;
|
|
|
|
// Klik untuk zoom/fly ke marker
|
|
item.onclick = function () {
|
|
var lat = parseFloat(f.lat || f.latitude);
|
|
var lng = parseFloat(f.lng || f.longitude);
|
|
map.flyTo([lat, lng], 18, { animate: true });
|
|
};
|
|
|
|
listContainer.appendChild(item);
|
|
});
|
|
}
|
|
|
|
// Gambar Chart.js
|
|
var ctx = document.getElementById('povertyChart').getContext('2d');
|
|
|
|
if (myPovertyChart) {
|
|
myPovertyChart.destroy();
|
|
}
|
|
|
|
var chartLabels = ['Sudah Menerima', 'Belum Menerima'];
|
|
var chartData = [sudahBantuan, belumBantuan];
|
|
var bgColors = ['#10b981', '#ef4444'];
|
|
|
|
if (totalKK === 0) {
|
|
chartLabels = ['Tidak Ada Data'];
|
|
chartData = [1];
|
|
bgColors = ['#475569'];
|
|
}
|
|
|
|
myPovertyChart = new Chart(ctx, {
|
|
type: 'doughnut',
|
|
data: {
|
|
labels: chartLabels,
|
|
datasets: [{
|
|
data: chartData,
|
|
backgroundColor: bgColors,
|
|
borderWidth: 0
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: {
|
|
position: 'bottom',
|
|
labels: {
|
|
color: '#cbd5e1',
|
|
font: {
|
|
family: 'Plus Jakarta Sans',
|
|
size: 10
|
|
},
|
|
boxWidth: 12
|
|
}
|
|
}
|
|
},
|
|
cutout: '65%'
|
|
}
|
|
});
|
|
}
|
|
|
|
function closeDashboard() {
|
|
document.getElementById('dashboard-sidebar').classList.remove('open');
|
|
} |