Upload files to "/"

This commit is contained in:
2026-06-11 11:22:21 +00:00
parent 7db4557167
commit c7f874b56f
+502
View File
@@ -0,0 +1,502 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebGIS Pemetaan Sosial</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css"/>
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css" />
<link rel="stylesheet" href="https://unpkg.com/leaflet-measure/dist/leaflet-measure.css" />
<style>
body {
margin: 0;
padding: 0;
background-color: #f8f9fa;
}
#map {
height: calc(100vh - 56px);
width: 100%;
}
.sidebar-panel {
height: calc(100vh - 56px);
overflow-y: auto;
background-color: #ffffff;
box-shadow: 2px 0 5px rgba(0,0,0,0.05);
}
.stat-box {
background: #e9ecef;
padding: 12px;
border-radius: 5px;
margin-bottom: 10px;
}
.stat-box p {
margin-bottom: 5px;
}
</style>
</head>
<body>
<nav class="navbar navbar-dark bg-primary shadow-sm">
<div class="container-fluid">
<span class="navbar-brand mb-0 h1 fw-bold">WebGIS Pemetaan Sosial</span>
<button class="btn btn-light btn-sm fw-bold" onclick="resetMap()">Reset Peta</button>
</div>
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-md-3 p-3 sidebar-panel">
<h5 class="fw-bold text-primary mb-3">Tambah Data</h5>
<label class="form-label small fw-bold mb-1">Jenis Data</label>
<select id="jenis" class="form-control form-control-sm mb-2" onchange="aturFormInput()">
<option value="ibadah">Rumah Ibadah</option>
<option value="miskin">Masyarakat Kurang Mampu</option>
</select>
<input type="text" id="nama" class="form-control form-control-sm mb-2" placeholder="Nama Tempat / Nama Lengkap">
<input type="text" id="alamat" class="form-control form-control-sm mb-2" placeholder="Alamat Lengkap">
<!-- UPDATE FORM IBADAH: Tambah Input Saldo -->
<div id="form-ibadah">
<label class="form-label small fw-bold mb-1">Saldo Kas Saat Ini (Rp)</label>
<input type="number" id="saldo" class="form-control form-control-sm mb-2" placeholder="Contoh: 500000" value="1000000">
<label class="form-label small fw-bold mb-1">Radius Jangkauan / Buffer (meter)</label>
<input type="number" id="radius" class="form-control form-control-sm mb-2" placeholder="Radius" value="300">
<label class="form-label small fw-bold mb-1">Foto Bangunan/Ibadah</label>
</div>
<div id="form-penduduk" style="display: none;">
<input type="text" id="nik" class="form-control form-control-sm mb-2" placeholder="NIK">
<label class="form-label small fw-bold mb-1">Jenis Kelamin</label>
<select id="jk" class="form-control form-control-sm mb-2">
<option value="Laki-laki">Laki-laki</option>
<option value="Perempuan">Perempuan</option>
</select>
<label class="form-label small fw-bold mb-1">Tanggal Lahir</label>
<input type="date" id="tglLahir" class="form-control form-control-sm mb-2">
<input type="text" id="pekerjaan" class="form-control form-control-sm mb-2" placeholder="Pekerjaan">
<input type="number" id="penghasilan" class="form-control form-control-sm mb-2" placeholder="Penghasilan per Bulan (Rp)">
<input type="number" id="tanggungan" class="form-control form-control-sm mb-2" placeholder="Jumlah Tanggungan">
<label class="form-label small fw-bold mb-1">Status Kepemilikan Rumah</label>
<select id="statusRumah" class="form-control form-control-sm mb-2">
<option value="Milik Sendiri">Milik Sendiri</option>
<option value="Kontrak">Kontrak</option>
<option value="Menumpang">Menumpang</option>
</select>
<label class="form-label small fw-bold mb-1">Kondisi Rumah</label>
<select id="kondisiRumah" class="form-control form-control-sm mb-2">
<option value="Layak">Layak</option>
<option value="Kurang Layak">Kurang Layak</option>
<option value="Tidak Layak">Tidak Layak</option>
</select>
<label class="form-label small fw-bold mb-1">Foto Penduduk</label>
<input type="file" id="fotoPenduduk" class="form-control form-control-sm mb-2" accept="image/*">
</div>
<input type="file" id="fotoRumah" class="form-control form-control-sm mb-3" accept="image/*">
<button class="btn btn-primary btn-sm w-100 mb-2" onclick="aktifkanTambah()">📌 Klik Peta untuk Menambah</button>
<button class="btn btn-success btn-sm w-100 mb-2" onclick="tampilkanLokasi()">📍 Lokasi Saya</button>
<button class="btn btn-secondary btn-sm w-100 mb-2" onclick="exportData()">📤 Export GeoJSON</button>
<label class="form-label small fw-bold text-muted mt-1 mb-1">Import File GeoJSON</label>
<input type="file" class="form-control form-control-sm mb-3" onchange="importData(event)">
<hr>
<div class="stat-box text-dark">
<h6 class="fw-bold mb-2">📊 Statistik</h6>
<div class="d-flex justify-content-between small">
<span>Rumah Ibadah:</span> <span id="countIbadah" class="fw-bold">0</span>
</div>
<div class="d-flex justify-content-between small">
<span>Masyarakat Kurang Mampu:</span> <span id="countMiskin" class="fw-bold">0</span>
</div>
</div>
<hr>
<h6 class="fw-bold text-secondary mb-2">📋 Data Tabel</h6>
<div style="max-height:250px; overflow-y:auto;">
<table class="table table-striped table-bordered table-sm small">
<thead class="table-dark">
<tr>
<th>Nama</th>
<th>NIK / Jenis</th>
<th>Alamat</th>
</tr>
</thead>
<tbody id="tabelPenduduk"></tbody>
</table>
</div>
</div>
<div class="col-md-9 p-0">
<div id="map"></div>
</div>
</div>
</div>
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
<script src="https://unpkg.com/leaflet-measure/dist/leaflet-measure.js"></script>
<script src="https://unpkg.com/leaflet-easyprint/dist/bundle.js"></script>
<script>
var map = L.map('map').setView([-0.0263, 109.3425], 13);
var osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
var esriSat = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}');
var layerIbadah = L.layerGroup().addTo(map);
var layerMiskin = L.layerGroup().addTo(map);
var layerRadius = L.layerGroup().addTo(map);
// UPDATE NEW LAYER: Layer untuk garis visualisasi bantuan kas
var layerBantuan = L.layerGroup().addTo(map);
L.control.layers(
{ "OpenStreetMap": osm, "Esri Satellite": esriSat },
{
"Rumah Ibadah": layerIbadah,
"Masyarakat Kurang Mampu": layerMiskin,
"Radius": layerRadius,
"Alur Bantuan Kas": layerBantuan
}
).addTo(map);
L.Control.geocoder().addTo(map);
L.control.measure({ primaryLengthUnit: 'meters', primaryAreaUnit: 'sqmeters' }).addTo(map);
L.easyPrint({ title: 'Cetak Peta', position: 'topleft', sizeModes: ['Current', 'A4Portrait', 'A4Landscape'] }).addTo(map);
var iconIbadah = L.icon({ iconUrl: 'https://cdn-icons-png.flaticon.com/512/684/684908.png', iconSize: [30, 30] });
var iconMiskin = L.icon({ iconUrl: 'https://cdn-icons-png.flaticon.com/512/1077/1077012.png', iconSize: [30, 30] });
var modeTambah = false;
var dataGeoJSON = [];
var tempLatLng;
// Konstanta batas minimum saldo untuk memicu tombol minta bantuan (Contoh: Rp 500.000)
var MIN_SALDO_BANTUAN = 500000;
function aturFormInput() {
var jenis = document.getElementById('jenis').value;
var formIbadah = document.getElementById('form-ibadah');
var formPenduduk = document.getElementById('form-penduduk');
if (jenis === 'ibadah') {
formIbadah.style.display = 'block';
formPenduduk.style.display = 'none';
} else {
formIbadah.style.display = 'none';
formPenduduk.style.display = 'block';
}
}
function aktifkanTambah() {
modeTambah = true;
alert("Klik pada peta untuk menambahkan data.");
}
map.on('click', function(e) {
if (!modeTambah) return;
modeTambah = false;
tempLatLng = e.latlng;
bacaFotoDanSimpan();
});
function bacaFotoDanSimpan() {
var fotoPendudukInput = document.getElementById('fotoPenduduk');
var fotoRumahInput = document.getElementById('fotoRumah');
var fotoPenduduk = "";
var fotoRumah = "";
var selesai1 = false;
var selesai2 = false;
function cekSelesai() {
if (selesai1 && selesai2) { tambahData(fotoPenduduk, fotoRumah); }
}
if (fotoPendudukInput && fotoPendudukInput.files[0]) {
var reader1 = new FileReader();
reader1.onload = function(e) { fotoPenduduk = e.target.result; selesai1 = true; cekSelesai(); }
reader1.readAsDataURL(fotoPendudukInput.files[0]);
} else { selesai1 = true; }
if (fotoRumahInput && fotoRumahInput.files[0]) {
var reader2 = new FileReader();
reader2.onload = function(e) { fotoRumah = e.target.result; selesai2 = true; cekSelesai(); }
reader2.readAsDataURL(fotoRumahInput.files[0]);
} else { selesai2 = true; }
cekSelesai();
}
function tambahData(fotoPenduduk, fotoRumah) {
var jenis = document.getElementById('jenis').value;
var nama = document.getElementById('nama').value || 'Tanpa Nama';
var alamat = document.getElementById('alamat').value || '-';
var nik = (jenis === 'miskin') ? (document.getElementById('nik').value || '-') : '-';
var jk = (jenis === 'miskin') ? document.getElementById('jk').value : '-';
var tglLahir = (jenis === 'miskin') ? (document.getElementById('tglLahir').value || '-') : '-';
var pekerjaan = (jenis === 'miskin') ? (document.getElementById('pekerjaan').value || '-') : '-';
var penghasilan = (jenis === 'miskin') ? (document.getElementById('penghasilan').value || '0') : '0';
var tanggungan = (jenis === 'miskin') ? (document.getElementById('tanggungan').value || '0') : '0';
var statusRumah = (jenis === 'miskin') ? document.getElementById('statusRumah').value : '-';
var kondisiRumah = (jenis === 'miskin') ? document.getElementById('kondisiRumah').value : '-';
var radius = (jenis === 'ibadah') ? (parseInt(document.getElementById('radius').value) || 300) : 0;
// Ambil nilai saldo
var saldo = (jenis === 'ibadah') ? (parseInt(document.getElementById('saldo').value) || 0) : 0;
var icon = jenis === 'ibadah' ? iconIbadah : iconMiskin;
var layer = jenis === 'ibadah' ? layerIbadah : layerMiskin;
var marker = L.marker(tempLatLng, { draggable: true, icon: icon }).addTo(layer);
// ID Unik untuk mencocokkan relasi antar-objek saat meminta bantuan
var idTempat = "id_" + new Date().getTime();
var popupContent = `<div style="min-width: 220px;">
<h6 class="fw-bold border-bottom pb-1" style="margin-bottom: 5px;">${nama}</h6>`;
if (jenis === 'miskin') {
popupContent += `
<table class="table table-sm table-borderless small m-0" style="font-size: 11px; line-height: 1.3;">
<tr><td><b>NIK:</b></td><td>${nik}</td></tr>
<tr><td><b>Gender:</b></td><td>${jk}</td></tr>
<tr><td><b>Lahir:</b></td><td>${tglLahir}</td></tr>
<tr><td><b>Alamat:</b></td><td>${alamat}</td></tr>
<tr><td><b>Kerja:</b></td><td>${pekerjaan}</td></tr>
<tr><td><b>Gaji:</b></td><td>Rp ${parseInt(penghasilan).toLocaleString('id-ID')}</td></tr>
<tr><td><b>Tanggungan:</b></td><td>${tanggungan} orang</td></tr>
<tr><td><b>Status Rmh:</b></td><td>${statusRumah}</td></tr>
<tr><td><b>Kondisi Rmh:</b></td><td>${kondisiRumah}</td></tr>
</table>
<div class="mt-2 text-center">
${fotoPenduduk ? `<img src="${fotoPenduduk}" class="img-thumbnail img-fluid mb-1" style="max-height:100px; object-fit: cover;">` : ''}
${fotoRumah ? `<img src="${fotoRumah}" class="img-thumbnail img-fluid" style="max-height:100px; object-fit: cover;">` : ''}
</div>`;
} else {
popupContent += `
<table class="table table-sm table-borderless small m-0" style="font-size: 11px; line-height: 1.3;">
<tr><td><b>Jenis:</b></td><td>Rumah Ibadah</td></tr>
<tr><td><b>Alamat:</b></td><td>${alamat}</td></tr>
<tr><td><b>Saldo Kas:</b></td><td class="${saldo < MIN_SALDO_BANTUAN ? 'text-danger fw-bold' : 'text-success'}">Rp ${saldo.toLocaleString('id-ID')}</td></tr>
<tr><td><b>Radius:</b></td><td>${radius} meter</td></tr>
</table>`;
// UPDATE POPUP: Jika saldo minim, tampilkan tombol aksi minta bantuan
if (saldo < MIN_SALDO_BANTUAN) {
popupContent += `
<div class="mt-2">
<button class="btn btn-danger btn-xs w-100 fw-bold" style="font-size:11px;" onclick="mintaBantuanKas('${idTempat}', ${tempLatLng.lat}, ${tempLatLng.lng})">
⚠️ Minta Bantuan Kas
</button>
</div>`;
}
popupContent += `
<div class="mt-2 text-center">
${fotoRumah ? `<img src="${fotoRumah}" class="img-thumbnail img-fluid" style="max-height:100px; object-fit: cover;">` : '<em>Tidak ada foto bangunan</em>'}
</div>`;
}
popupContent += `</div>`;
marker.bindPopup(popupContent);
if (jenis === 'ibadah') {
var circle = L.circle(tempLatLng, { radius: radius, color: 'blue', fillOpacity: 0.15 }).addTo(layerRadius);
marker.on('drag', function() {
circle.setLatLng(marker.getLatLng());
});
}
dataGeoJSON.push({
type: "Feature",
id: idTempat,
properties: { jenis, nama, nik, jk, tglLahir, alamat, pekerjaan, penghasilan, tanggungan, statusRumah, kondisiRumah, fotoPenduduk, fotoRumah, radius, saldo },
geometry: { type: "Point", coordinates: [tempLatLng.lng, tempLatLng.lat] }
});
tambahKeTabel(nama, (jenis === 'miskin' ? nik : 'Rumah Ibadah'), alamat);
updateStatistik();
simpanLocalStorage();
}
// --- UPDATE BARU: FUNGSI LOGIKA MINTA BANTUAN ---
function mintaBantuanKas(idPeminta, latPeminta, lngPeminta) {
layerBantuan.clearLayers(); // Reset garis bantuan lama
var peminta = dataGeoJSON.find(f => f.id === idPeminta);
var donaturTerdekat = null;
var jarakTerdekat = Infinity;
// Cari rumah ibadah lain yang saldonya cukup (>= MIN_SALDO_BANTUAN)
dataGeoJSON.forEach(function(tempat) {
if (tempat.properties.jenis === 'ibadah' && tempat.id !== idPeminta) {
if (tempat.properties.saldo >= MIN_SALDO_BANTUAN) {
var latDonatur = tempat.geometry.coordinates[1];
var lngDonatur = tempat.geometry.coordinates[0];
// Hitung jarak (Formula Euclidean sederhana untuk Leaflet LatLng)
var jarak = map.distance([latPeminta, lngPeminta], [latDonatur, lngDonatur]);
if (jarak < jarakTerdekat) {
jarakTerdekat = jarak;
donaturTerdekat = tempat;
}
}
}
});
if (donaturTerdekat) {
var latDonatur = donaturTerdekat.geometry.coordinates[1];
var lngDonatur = donaturTerdekat.geometry.coordinates[0];
// Buat garis Polyline putus-putus merah tebal yang menghubungkan kedua lokasi
var garisBantuan = L.polyline([
[latPeminta, lngPeminta],
[latDonatur, lngDonatur]
], {
color: 'red',
weight: 4,
dashArray: '10, 10',
className: 'animated-line' // Jika ingin menggunakan animasi CSS tambahan
}).addTo(layerBantuan);
// Zoom out/fit peta agar memuat kedua titik koordinat tersebut
var group = new L.featureGroup([
L.marker([latPeminta, lngPeminta]),
L.marker([latDonatur, lngDonatur])
]);
map.fitBounds(group.getBounds().pad(0.2));
alert(`Permintaan Bantuan Berhasil!\n\nRumah Ibadah "${peminta.properties.nama}" membutuhkan saldo.\nSistem merekomendasikan bantuan dari rumah ibadah terdekat:\n👉 "${donaturTerdekat.properties.nama}"\nJarak: ${(jarakTerdekat / 1000).toFixed(2)} Km.\n\n(Jalur visualisasi merah putus-putus telah ditampilkan di peta)`);
} else {
alert("Maaf, saat ini tidak ditemukan Rumah Ibadah alternatif terdekat yang memiliki saldo kas mencukupi untuk memberikan bantuan.");
}
}
function tambahKeTabel(nama, infoKolom2, alamat) {
var tabel = document.getElementById('tabelPenduduk');
tabel.innerHTML += `<tr><td>${nama}</td><td>${infoKolom2}</td><td>${alamat}</td></tr>`;
}
function updateStatistik() {
document.getElementById('countIbadah').innerText = layerIbadah.getLayers().length;
document.getElementById('countMiskin').innerText = layerMiskin.getLayers().length;
}
function simpanLocalStorage() {
localStorage.setItem('dataPemetaan', JSON.stringify(dataGeoJSON));
}
function exportData() {
if (dataGeoJSON.length === 0) { alert("Belum ada data."); return; }
var geojsonWrapper = { type: "FeatureCollection", features: dataGeoJSON };
var blob = new Blob([JSON.stringify(geojsonWrapper, null, 2)], { type: "application/json" });
var link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = "data_pemetaan_sosial.geojson";
link.click();
}
function importData(event) {
var file = event.target.files[0];
if (!file) return;
var reader = new FileReader();
reader.onload = function(e) {
try {
var geojson = JSON.parse(e.target.result);
var features = geojson.features ? geojson.features : (Array.isArray(geojson) ? geojson : []);
features.forEach(function(feat) {
tempLatLng = L.latLng(feat.geometry.coordinates[1], feat.geometry.coordinates[0]);
document.getElementById('jenis').value = feat.properties.jenis || 'miskin';
aturFormInput();
document.getElementById('nama').value = feat.properties.nama || '';
document.getElementById('alamat').value = feat.properties.alamat || '';
if(feat.properties.jenis === 'miskin'){
document.getElementById('nik').value = feat.properties.nik || '';
document.getElementById('jk').value = feat.properties.jk || 'Laki-laki';
document.getElementById('tglLahir').value = feat.properties.tglLahir || '';
document.getElementById('pekerjaan').value = feat.properties.pekerjaan || '';
document.getElementById('penghasilan').value = feat.properties.penghasilan || 0;
document.getElementById('tanggungan').value = feat.properties.tanggungan || 0;
document.getElementById('statusRumah').value = feat.properties.statusRumah || 'Milik Sendiri';
document.getElementById('kondisiRumah').value = feat.properties.kondisiRumah || 'Layak';
} else {
document.getElementById('radius').value = feat.properties.radius || 300;
document.getElementById('saldo').value = feat.properties.saldo || 0; // Import saldo
}
tambahData(feat.properties.fotoPenduduk, feat.properties.fotoRumah);
});
alert("File GeoJSON sukses di-import!");
} catch (err) { alert("Format berkas GeoJSON tidak valid!"); }
};
reader.readAsText(file);
}
function tampilkanLokasi() {
map.locate({ setView: true, maxZoom: 16 });
map.on('locationfound', function(e) {
L.marker(e.latlng).addTo(map).bindPopup("<b>Lokasi Anda Sekarang</b>").openPopup();
});
}
function resetMap() {
layerBantuan.clearLayers();
map.setView([-0.0263, 109.3425], 13);
}
window.onload = function() {
aturFormInput();
var dataLokal = localStorage.getItem('dataPemetaan');
if (dataLokal) {
var parsedData = JSON.parse(dataLokal);
parsedData.forEach(function(feat) {
tempLatLng = L.latLng(feat.geometry.coordinates[1], feat.geometry.coordinates[0]);
document.getElementById('jenis').value = feat.properties.jenis;
aturFormInput();
document.getElementById('nama').value = feat.properties.nama;
document.getElementById('alamat').value = feat.properties.alamat;
if(feat.properties.jenis === 'miskin') {
document.getElementById('nik').value = feat.properties.nik;
document.getElementById('jk').value = feat.properties.jk;
document.getElementById('tglLahir').value = feat.properties.tglLahir;
document.getElementById('pekerjaan').value = feat.properties.pekerjaan;
document.getElementById('penghasilan').value = feat.properties.penghasilan;
document.getElementById('tanggungan').value = feat.properties.tanggungan;
document.getElementById('statusRumah').value = feat.properties.statusRumah;
document.getElementById('kondisiRumah').value = feat.properties.kondisiRumah;
} else {
document.getElementById('radius').value = feat.properties.radius;
document.getElementById('saldo').value = feat.properties.saldo || 0; // Load saldo
}
tambahData(feat.properties.fotoPenduduk, feat.properties.fotoRumah);
});
}
};
</script>
</body>
</html>