feat : portal page
This commit is contained in:
+82
-312
@@ -1,329 +1,99 @@
|
|||||||
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="id">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>WebGIS Pro - Layer Control SPBU</title>
|
<title>Portal Tugas Sistem Informasi Geografis - Muhammad Iqbal Maulana</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://unpkg.com/@geoman-io/leaflet-geoman-free@latest/dist/leaflet-geoman.css" />
|
|
||||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
|
||||||
<script src="https://unpkg.com/@geoman-io/leaflet-geoman-free@latest/dist/leaflet-geoman.min.js"></script>
|
|
||||||
<script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
html, body, #map { height: 100%; margin: 0; padding: 0; font-family: 'Segoe UI', sans-serif; }
|
body { font-family: 'Plus Jakarta Sans', sans-serif; }
|
||||||
.form-container { width: 220px; padding: 10px; }
|
|
||||||
.btn-simpan { background: #28a745; color: white; border: none; padding: 8px; width: 100%; cursor: pointer; border-radius: 4px; font-weight: bold; margin-top: 10px; }
|
|
||||||
.btn-update { background: #ffc107; color: black; border: none; padding: 8px; width: 100%; cursor: pointer; border-radius: 4px; font-weight: bold; margin-top: 10px; }
|
|
||||||
select, input { width: 100%; margin-bottom: 8px; padding: 5px; box-sizing: border-box; }
|
|
||||||
|
|
||||||
.ikon-edit-atribut { display: flex; align-items: center; justify-content: center; }
|
|
||||||
.ikon-edit-atribut::after { content: '📝'; font-size: 16px; }
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="bg-slate-50 text-slate-800 min-h-screen flex flex-col selection:bg-emerald-500 selection:text-white">
|
||||||
|
|
||||||
<div id="map"></div>
|
<header class="bg-white border-b border-slate-200/80 sticky top-0 z-50 backdrop-blur-md bg-white/80">
|
||||||
|
<div class="max-w-5xl mx-auto px-6 py-4 flex flex-col sm:flex-row justify-between items-center gap-4">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="w-10 h-10 bg-gradient-to-br from-emerald-500 to-teal-600 rounded-xl flex items-center justify-center text-white font-extrabold shadow-md shadow-emerald-500/20">
|
||||||
|
SIG
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h1 class="font-bold text-slate-900 text-sm leading-tight">Portal Tugas SIG</h1>
|
||||||
|
<p class="text-[11px] text-slate-500 font-medium tracking-wide uppercase">Informatika Universitas Tanjungpura</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-center sm:text-right">
|
||||||
|
<p class="text-sm font-semibold text-slate-800">Muhammad Iqbal Maulana</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
<script>
|
<main class="flex-grow max-w-5xl mx-auto w-full px-6 py-12 space-y-12">
|
||||||
const map = L.map('map').setView([-0.0541467, 109.3491684], 14);
|
|
||||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
|
|
||||||
|
|
||||||
const layer_spbu_24 = L.layerGroup();
|
<section class="text-center max-w-2xl mx-auto space-y-4">
|
||||||
const layer_spbu_non_24 = L.layerGroup();
|
<span class="px-3 py-1 bg-emerald-50 border border-emerald-200 text-emerald-700 text-xs font-bold rounded-full uppercase tracking-wider inline-block">UAS Evaluasi Praktis</span>
|
||||||
|
<h2 class="text-3xl sm:text-4xl font-extrabold text-slate-900 tracking-tight leading-tight">
|
||||||
|
Kompilasi Sistem Informasi Geografis Terapan
|
||||||
|
</h2>
|
||||||
|
<p class="text-sm text-slate-500 font-light max-w-lg mx-auto leading-relaxed">
|
||||||
|
Aplikasi WebGIS produksi terintegrasi yang dikembangkan untuk memenuhi tugas akhir semester, berjalan di atas infrastruktur kontainer peladen Docker Coolify.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
L.control.layers(null, {
|
<section class="grid grid-cols-1 md:grid-cols-2 gap-6 pt-4">
|
||||||
"SPBU Buka 24 Jam": layer_spbu_24,
|
|
||||||
"SPBU Tidak Buka 24 Jam": layer_spbu_non_24
|
|
||||||
}).addTo(map);
|
|
||||||
|
|
||||||
map.pm.addControls({
|
<div class="bg-white border border-slate-200 rounded-3xl p-6 shadow-sm hover:shadow-xl hover:border-slate-300 transition-all duration-300 flex flex-col justify-between group">
|
||||||
position: 'topleft',
|
<div class="space-y-4">
|
||||||
drawMarker: true,
|
<div class="w-12 h-12 bg-amber-50 border border-amber-200 text-amber-600 rounded-2xl flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
|
||||||
drawPolyline: true,
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 22V2a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v20"/><path d="M18 12h4"/><path d="M10 6h4"/><path d="M12 2v4"/><circle cx="12" cy="12" r="2"/></svg>
|
||||||
drawPolygon: true,
|
</div>
|
||||||
drawCircle: false, // Menyembunyikan alat Lingkaran (Circle)
|
<div class="space-y-1">
|
||||||
drawCircleMarker: false, // Menyembunyikan alat Titik Lingkaran (CircleMarker)
|
<h3 class="font-bold text-lg text-slate-900 tracking-tight">Web Pemetaan Pom Bensin & Wilayah</h3>
|
||||||
drawRectangle: false, // Menyembunyikan alat Persegi (Rectangle)
|
<p class="text-xs font-medium text-amber-600 uppercase tracking-wide">Analisis Geospasial Infrastruktur</p>
|
||||||
drawText: false, // Menyembunyikan alat Teks
|
</div>
|
||||||
removalMode: true,
|
<p class="text-xs text-slate-400 font-light leading-relaxed">
|
||||||
editMode: true,
|
Sistem informasi spasial yang memetakan persebaran Stasiun Pengisian Bahan Bakar Umum (SPBU) di area Pontianak, dilengkapi zonasi wilayah jangkauan pelayanan guna analisis efisiensi fasilitas publik.
|
||||||
dragMode: true
|
</p>
|
||||||
});
|
</div>
|
||||||
map.pm.setGlobalOptions({ allowSelfIntersection: false });
|
<div class="pt-6 mt-6 border-t border-slate-100 flex items-center justify-between">
|
||||||
|
<a href="http://e8wwc0wgwsws0w4ggoc8g88g.203.24.51.230.sslip.io/portal.html" target="_blank" class="px-4 py-2.5 bg-slate-900 hover:bg-slate-800 text-white text-xs font-bold rounded-xl transition-all shadow-sm flex items-center gap-1">
|
||||||
|
Buka Aplikasi
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
let is_edit_atribut_mode = false;
|
<div class="bg-white border border-slate-200 rounded-3xl p-6 shadow-sm hover:shadow-xl hover:border-slate-300 transition-all duration-300 flex flex-col justify-between group">
|
||||||
map.pm.Toolbar.createCustomControl({
|
<div class="space-y-4">
|
||||||
name: 'EditAtributData',
|
<div class="w-12 h-12 bg-emerald-50 border border-emerald-200 text-emerald-600 rounded-2xl flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
|
||||||
block: 'custom',
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="3"/></svg>
|
||||||
title: 'Edit Nama & Status',
|
</div>
|
||||||
className: 'ikon-edit-atribut',
|
<div class="space-y-1">
|
||||||
toggle: true,
|
<h3 class="font-bold text-lg text-slate-900 tracking-tight">WebGIS PovertyMap</h3>
|
||||||
onClick: () => {
|
<p class="text-xs font-medium text-emerald-600 uppercase tracking-wide">Transparansi Sosial & Validasi BPS</p>
|
||||||
is_edit_atribut_mode = !is_edit_atribut_mode;
|
</div>
|
||||||
}
|
<p class="text-xs text-slate-400 font-light leading-relaxed">
|
||||||
});
|
Sistem Informasi Geografis pemetaan klaster kemiskinan masyarakat Kota Pontianak terintegrasi 10 parameter BPS. Menghubungkan titik hunian warga dengan pangkalan rumah ibadah terdekat secara asinkron.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="pt-6 mt-6 border-t border-slate-100 flex items-center justify-between">
|
||||||
|
<a href="http://l08g8oso4gk0wc400cggog4s.203.24.51.230.sslip.io/index.html" target="_blank" class="px-4 py-2.5 bg-emerald-600 hover:bg-emerald-700 text-white text-xs font-bold rounded-xl transition-all shadow-sm flex items-center gap-1">
|
||||||
|
Buka Aplikasi
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
// Menentukan gaya warna jalan berdasarkan status
|
</section>
|
||||||
function gaya_jalan(status) {
|
|
||||||
if (status === 'Nasional') return { color: 'red', weight: 6 };
|
|
||||||
if (status === 'Provinsi') return { color: 'blue', weight: 4 };
|
|
||||||
return { color: 'green', weight: 2 };
|
|
||||||
}
|
|
||||||
|
|
||||||
let sedang_memproses = false;
|
</main>
|
||||||
|
|
||||||
// Menangani event penggambaran objek spasial baru dari Leaflet-Geoman
|
<footer class="bg-white border-t border-slate-200 text-center py-6 text-xs font-medium text-slate-400 mt-auto">
|
||||||
map.on('pm:create', function(e) {
|
© 2026 Jurusan Informatika Universitas Tanjungpura. Dibuat dengan dedikasi penuh.
|
||||||
// Mencegah klik ganda yang memicu pembuatan layer ganda bertumpuk
|
</footer>
|
||||||
if (sedang_memproses) { map.removeLayer(e.layer); return; }
|
|
||||||
sedang_memproses = true;
|
|
||||||
setTimeout(() => { sedang_memproses = false; }, 500);
|
|
||||||
|
|
||||||
const layer = e.layer;
|
|
||||||
const shape = e.shape;
|
|
||||||
let popup_content = "";
|
|
||||||
|
|
||||||
if (shape === 'Line') {
|
|
||||||
const geojson_mentah = layer.toGeoJSON();
|
|
||||||
// Melakukan escape karakter kutip agar string JSON aman disuntikkan ke atribut onclick HTML
|
|
||||||
const geo_data = JSON.stringify(geojson_mentah.geometry).replace(/"/g, '"');
|
|
||||||
const panjang_meter = turf.length(geojson_mentah, {units: 'kilometers'}) * 1000;
|
|
||||||
|
|
||||||
popup_content = `<div class="form-container"><h4>Tambah Jalan Baru</h4>
|
|
||||||
<input type="text" id="nama_jln" placeholder="Nama Jalan">
|
|
||||||
<select id="stat_jln"><option value="Nasional">Nasional</option><option value="Provinsi">Provinsi</option><option value="Kabupaten">Kabupaten</option></select>
|
|
||||||
<p>Panjang: <b>${panjang_meter.toFixed(2)} m</b></p>
|
|
||||||
<button class="btn-simpan" onclick="simpan_baru('jalan', ${panjang_meter}, '${geo_data}')">Simpan Baru</button></div>`;
|
|
||||||
}
|
|
||||||
else if (shape === 'Polygon') {
|
|
||||||
const geojson_mentah = layer.toGeoJSON();
|
|
||||||
const geo_data = JSON.stringify(geojson_mentah.geometry).replace(/"/g, '"');
|
|
||||||
const luas_meter_p = turf.area(geojson_mentah);
|
|
||||||
|
|
||||||
popup_content = `<div class="form-container"><h4>Tambah Persil Baru</h4>
|
|
||||||
<input type="text" id="no_psl" placeholder="Nomor Persil">
|
|
||||||
<select id="stat_psl"><option value="SHM">SHM</option><option value="HGB">HGB</option><option value="HGU">HGU</option><option value="HP">HP</option></select>
|
|
||||||
<p>Luas: <b>${luas_meter_p.toFixed(2)} m²</b></p>
|
|
||||||
<button class="btn-simpan" onclick="simpan_baru('persil', ${luas_meter_p}, '${geo_data}')">Simpan Baru</button></div>`;
|
|
||||||
layer.setStyle({fillColor: 'yellow', fillOpacity: 0.5, color: 'black'});
|
|
||||||
}
|
|
||||||
else if (shape === 'Marker') {
|
|
||||||
const titik_kordinat = layer.getLatLng();
|
|
||||||
popup_content = `<div class="form-container"><h4>Tambah SPBU Baru</h4>
|
|
||||||
<input type="text" id="nama_spbu" placeholder="Nama SPBU">
|
|
||||||
<input type="text" id="hp_spbu" placeholder="No. HP">
|
|
||||||
<select id="stat_spbu">
|
|
||||||
<option value="24 Jam">Buka 24 Jam</option>
|
|
||||||
<option value="Tidak 24 Jam">Tidak Buka 24 Jam</option>
|
|
||||||
</select>
|
|
||||||
<button class="btn-simpan" onclick="simpan_baru('spbu', 0, null, ${titik_kordinat.lat}, ${titik_kordinat.lng})">Simpan Baru</button></div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
layer.bindPopup(popup_content);
|
|
||||||
setTimeout(() => { layer.openPopup(); }, 100);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Menangani event penghapusan data spasial
|
|
||||||
map.on('pm:remove', function(e) {
|
|
||||||
const layer = e.layer;
|
|
||||||
if (layer.db_id && layer.db_type) {
|
|
||||||
if(confirm('Yakin ingin menghapus data ini dari Database permanen?')) {
|
|
||||||
fetch('hapus_feature.php', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ id: layer.db_id, type: layer.db_type })
|
|
||||||
}).then(r => r.json()).then(res => {
|
|
||||||
if(res.status !== 'success') { alert('Gagal Hapus: ' + res.message); location.reload(); }
|
|
||||||
}).catch(err => {
|
|
||||||
alert('Kesalahan jaringan saat menghapus data.');
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Mengumpulkan dan mengirimkan payload data baru ke server PHP
|
|
||||||
window.simpan_baru = function(type, hitungan, geo, lat = null, lng = null) {
|
|
||||||
let payload = { type: type };
|
|
||||||
|
|
||||||
// Membentuk struktur data (payload) berdasarkan tipe objek
|
|
||||||
if (type === 'jalan') {
|
|
||||||
payload.nama = document.getElementById('nama_jln').value;
|
|
||||||
payload.status = document.getElementById('stat_jln').value;
|
|
||||||
payload.besaran = hitungan;
|
|
||||||
payload.geojson = geo;
|
|
||||||
} else if (type === 'persil') {
|
|
||||||
payload.nama = document.getElementById('no_psl').value;
|
|
||||||
payload.status = document.getElementById('stat_psl').value;
|
|
||||||
payload.besaran = hitungan;
|
|
||||||
payload.geojson = geo;
|
|
||||||
} else if (type === 'spbu') {
|
|
||||||
payload.nama = document.getElementById('nama_spbu').value;
|
|
||||||
payload.jam24 = document.getElementById('stat_spbu').value;
|
|
||||||
payload.hp = document.getElementById('hp_spbu').value;
|
|
||||||
payload.lat = lat;
|
|
||||||
payload.lng = lng;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Memvalidasi apakah kolom nama sudah diisi sebelum menghubungi server
|
|
||||||
if (!payload.nama || payload.nama.trim() === '') {
|
|
||||||
alert("Informasi nama/identitas wajib diisi!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mengeksekusi permintaan pengiriman data asinkronus ke API backend
|
|
||||||
fetch('simpan_feature.php', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify(payload)
|
|
||||||
})
|
|
||||||
.then(r => r.json()).then(res => {
|
|
||||||
if(res.status === 'success') {
|
|
||||||
alert('Berhasil Disimpan!');
|
|
||||||
location.reload();
|
|
||||||
} else {
|
|
||||||
alert('Gagal: ' + res.message);
|
|
||||||
console.error(res);
|
|
||||||
}
|
|
||||||
}).catch(err => {
|
|
||||||
alert('Terjadi kesalahan jaringan atau server tidak merespons.');
|
|
||||||
console.error('Catch Error:', err);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Memperbarui atribut data yang sudah ada
|
|
||||||
window.update_data = function(type, id) {
|
|
||||||
let nama_baru = document.getElementById('edit_nama').value;
|
|
||||||
if (!nama_baru || nama_baru.trim() === '') {
|
|
||||||
alert("Nama tidak boleh kosong!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let payload = {
|
|
||||||
type: type,
|
|
||||||
id: id,
|
|
||||||
nama: nama_baru,
|
|
||||||
status: document.getElementById('edit_status').value
|
|
||||||
};
|
|
||||||
|
|
||||||
fetch('edit_feature.php', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify(payload)
|
|
||||||
})
|
|
||||||
.then(r => r.json()).then(res => {
|
|
||||||
if(res.status === 'success') {
|
|
||||||
alert('Berhasil Diupdate!');
|
|
||||||
location.reload();
|
|
||||||
} else {
|
|
||||||
alert('Gagal Update: ' + res.message);
|
|
||||||
}
|
|
||||||
}).catch(err => {
|
|
||||||
alert('Gagal menghubungi server untuk pembaharuan.');
|
|
||||||
console.error('Catch Error:', err);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Merender seluruh data spasial dari pangkalan data ke peta Leaflet
|
|
||||||
function muat_data_feature() {
|
|
||||||
fetch('tampil_feature.php')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(res => {
|
|
||||||
if (res.status === "success") {
|
|
||||||
|
|
||||||
// Merender data garis jalan
|
|
||||||
res.jalan.forEach(j => {
|
|
||||||
const geojson_obj = JSON.parse(j.geojson);
|
|
||||||
L.geoJSON(geojson_obj, {
|
|
||||||
style: gaya_jalan(j.status_jalan),
|
|
||||||
onEachFeature: function(feature, layer) {
|
|
||||||
layer.db_id = j.id; layer.db_type = 'jalan';
|
|
||||||
layer.on('click', function(e) {
|
|
||||||
let html_konten = is_edit_atribut_mode ?
|
|
||||||
`<div class="form-container"><h4>Edit Atribut Jalan</h4>
|
|
||||||
<input type="text" id="edit_nama" value="${j.nama_jalan}">
|
|
||||||
<select id="edit_status">
|
|
||||||
<option value="Nasional" ${j.status_jalan=='Nasional'?'selected':''}>Nasional</option>
|
|
||||||
<option value="Provinsi" ${j.status_jalan=='Provinsi'?'selected':''}>Provinsi</option>
|
|
||||||
<option value="Kabupaten" ${j.status_jalan=='Kabupaten'?'selected':''}>Kabupaten</option>
|
|
||||||
</select>
|
|
||||||
<button class="btn-update" onclick="update_data('jalan', ${j.id})">Simpan Perubahan</button></div>` :
|
|
||||||
`<b>Jalan: ${j.nama_jalan}</b><br>Status: ${j.status_jalan}<br>Panjang: ${j.panjang_m} m`;
|
|
||||||
L.popup().setLatLng(e.latlng).setContent(html_konten).openOn(map);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).addTo(map);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Merender data poligon persil tanah
|
|
||||||
res.persil.forEach(p => {
|
|
||||||
const geojson_obj = JSON.parse(p.geojson);
|
|
||||||
L.geoJSON(geojson_obj, {
|
|
||||||
style: { color: 'orange', weight: 2, fillOpacity: 0.4 },
|
|
||||||
onEachFeature: function(feature, layer) {
|
|
||||||
layer.db_id = p.id; layer.db_type = 'persil';
|
|
||||||
layer.on('click', function(e) {
|
|
||||||
let html_konten = is_edit_atribut_mode ?
|
|
||||||
`<div class="form-container"><h4>Edit Atribut Persil</h4>
|
|
||||||
<input type="text" id="edit_nama" value="${p.nomor_persil}">
|
|
||||||
<select id="edit_status">
|
|
||||||
<option value="SHM" ${p.status_milik=='SHM'?'selected':''}>SHM</option>
|
|
||||||
<option value="HGB" ${p.status_milik=='HGB'?'selected':''}>HGB</option>
|
|
||||||
<option value="HGU" ${p.status_milik=='HGU'?'selected':''}>HGU</option>
|
|
||||||
<option value="HP" ${p.status_milik=='HP'?'selected':''}>HP</option>
|
|
||||||
</select>
|
|
||||||
<button class="btn-update" onclick="update_data('persil', ${p.id})">Simpan Perubahan</button></div>` :
|
|
||||||
`<b>Nomor Persil: ${p.nomor_persil}</b><br>Status: ${p.status_milik}<br>Luas: ${p.luas_m2} m²`;
|
|
||||||
L.popup().setLatLng(e.latlng).setContent(html_konten).openOn(map);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).addTo(map);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Merender titik lokasi SPBU ke dalam grup layer yang berbeda
|
|
||||||
res.spbu.forEach(s => {
|
|
||||||
const spbu_marker = L.marker([parseFloat(s.lat), parseFloat(s.lng)]);
|
|
||||||
spbu_marker.db_id = s.id;
|
|
||||||
spbu_marker.db_type = 'spbu';
|
|
||||||
|
|
||||||
spbu_marker.on('click', function(e) {
|
|
||||||
let html_konten = is_edit_atribut_mode ?
|
|
||||||
`<div class="form-container"><h4>Edit Atribut SPBU</h4>
|
|
||||||
<input type="text" id="edit_nama" value="${s.nama_spbu}">
|
|
||||||
<select id="edit_status">
|
|
||||||
<option value="24 Jam" ${s.is_24jam=='24 Jam'?'selected':''}>Buka 24 Jam</option>
|
|
||||||
<option value="Tidak 24 Jam" ${s.is_24jam=='Tidak 24 Jam'?'selected':''}>Tidak Buka 24 Jam</option>
|
|
||||||
</select>
|
|
||||||
<button class="btn-update" onclick="update_data('spbu', ${s.id})">Simpan Perubahan</button></div>` :
|
|
||||||
`<b>SPBU: ${s.nama_spbu}</b><br>Status: ${s.is_24jam}<br>No. HP: ${s.no_hp}`;
|
|
||||||
L.popup().setLatLng(e.latlng).setContent(html_konten).openOn(map);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (s.is_24jam === '24 Jam') {
|
|
||||||
spbu_marker.addTo(layer_spbu_24);
|
|
||||||
} else {
|
|
||||||
spbu_marker.addTo(layer_spbu_non_24);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
layer_spbu_24.addTo(map);
|
|
||||||
layer_spbu_non_24.addTo(map);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
alert("Gagal memuat data peta: " + res.message);
|
|
||||||
console.error("Kesalahan Sistem:", res.message);
|
|
||||||
}
|
|
||||||
}).catch(err => {
|
|
||||||
console.error('Kesalahan Jaringan: Gagal merender data peladen', err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
muat_data_feature();
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+312
-80
@@ -1,97 +1,329 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="id">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Portal Tugas Sistem Informasi Geografis - Muhammad Iqbal Maulana</title>
|
<title>WebGIS Pro - Layer Control SPBU</title>
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/@geoman-io/leaflet-geoman-free@latest/dist/leaflet-geoman.css" />
|
||||||
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||||
|
<script src="https://unpkg.com/@geoman-io/leaflet-geoman-free@latest/dist/leaflet-geoman.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body { font-family: 'Plus Jakarta Sans', sans-serif; }
|
html, body, #map { height: 100%; margin: 0; padding: 0; font-family: 'Segoe UI', sans-serif; }
|
||||||
|
.form-container { width: 220px; padding: 10px; }
|
||||||
|
.btn-simpan { background: #28a745; color: white; border: none; padding: 8px; width: 100%; cursor: pointer; border-radius: 4px; font-weight: bold; margin-top: 10px; }
|
||||||
|
.btn-update { background: #ffc107; color: black; border: none; padding: 8px; width: 100%; cursor: pointer; border-radius: 4px; font-weight: bold; margin-top: 10px; }
|
||||||
|
select, input { width: 100%; margin-bottom: 8px; padding: 5px; box-sizing: border-box; }
|
||||||
|
|
||||||
|
.ikon-edit-atribut { display: flex; align-items: center; justify-content: center; }
|
||||||
|
.ikon-edit-atribut::after { content: '📝'; font-size: 16px; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-slate-50 text-slate-800 min-h-screen flex flex-col selection:bg-emerald-500 selection:text-white">
|
<body>
|
||||||
|
|
||||||
<header class="bg-white border-b border-slate-200/80 sticky top-0 z-50 backdrop-blur-md bg-white/80">
|
<div id="map"></div>
|
||||||
<div class="max-w-5xl mx-auto px-6 py-4 flex flex-col sm:flex-row justify-between items-center gap-4">
|
|
||||||
<div class="flex items-center gap-3">
|
|
||||||
<div class="w-10 h-10 bg-gradient-to-br from-emerald-500 to-teal-600 rounded-xl flex items-center justify-center text-white font-extrabold shadow-md shadow-emerald-500/20">
|
|
||||||
SIG
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h1 class="font-bold text-slate-900 text-sm leading-tight">Portal Tugas SIG</h1>
|
|
||||||
<p class="text-[11px] text-slate-500 font-medium tracking-wide uppercase">Informatika Universitas Tanjungpura</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="text-center sm:text-right">
|
|
||||||
<p class="text-sm font-semibold text-slate-800">Muhammad Iqbal Maulana</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main class="flex-grow max-w-5xl mx-auto w-full px-6 py-12 space-y-12">
|
<script>
|
||||||
|
const map = L.map('map').setView([-0.0541467, 109.3491684], 14);
|
||||||
|
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
|
||||||
|
|
||||||
<section class="text-center max-w-2xl mx-auto space-y-4">
|
const layer_spbu_24 = L.layerGroup();
|
||||||
<span class="px-3 py-1 bg-emerald-50 border border-emerald-200 text-emerald-700 text-xs font-bold rounded-full uppercase tracking-wider inline-block">UAS Evaluasi Praktis</span>
|
const layer_spbu_non_24 = L.layerGroup();
|
||||||
<h2 class="text-3xl sm:text-4xl font-extrabold text-slate-900 tracking-tight leading-tight">
|
|
||||||
Kompilasi Sistem Informasi Geografis Terapan
|
|
||||||
</h2>
|
|
||||||
<p class="text-sm text-slate-500 font-light max-w-lg mx-auto leading-relaxed">
|
|
||||||
Aplikasi WebGIS produksi terintegrasi yang dikembangkan untuk memenuhi tugas akhir semester, berjalan di atas infrastruktur kontainer peladen Docker Coolify.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="grid grid-cols-1 md:grid-cols-2 gap-6 pt-4">
|
L.control.layers(null, {
|
||||||
|
"SPBU Buka 24 Jam": layer_spbu_24,
|
||||||
|
"SPBU Tidak Buka 24 Jam": layer_spbu_non_24
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
<div class="bg-white border border-slate-200 rounded-3xl p-6 shadow-sm hover:shadow-xl hover:border-slate-300 transition-all duration-300 flex flex-col justify-between group">
|
map.pm.addControls({
|
||||||
<div class="space-y-4">
|
position: 'topleft',
|
||||||
<div class="w-12 h-12 bg-amber-50 border border-amber-200 text-amber-600 rounded-2xl flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
|
drawMarker: true,
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 22V2a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v20"/><path d="M18 12h4"/><path d="M10 6h4"/><path d="M12 2v4"/><circle cx="12" cy="12" r="2"/></svg>
|
drawPolyline: true,
|
||||||
</div>
|
drawPolygon: true,
|
||||||
<div class="space-y-1">
|
drawCircle: false, // Menyembunyikan alat Lingkaran (Circle)
|
||||||
<h3 class="font-bold text-lg text-slate-900 tracking-tight">Web Pemetaan Pom Bensin & Wilayah</h3>
|
drawCircleMarker: false, // Menyembunyikan alat Titik Lingkaran (CircleMarker)
|
||||||
<p class="text-xs font-medium text-amber-600 uppercase tracking-wide">Analisis Geospasial Infrastruktur</p>
|
drawRectangle: false, // Menyembunyikan alat Persegi (Rectangle)
|
||||||
</div>
|
drawText: false, // Menyembunyikan alat Teks
|
||||||
<p class="text-xs text-slate-400 font-light leading-relaxed">
|
removalMode: true,
|
||||||
Sistem informasi spasial yang memetakan persebaran Stasiun Pengisian Bahan Bakar Umum (SPBU) di area Pontianak, dilengkapi zonasi wilayah jangkauan pelayanan guna analisis efisiensi fasilitas publik.
|
editMode: true,
|
||||||
</p>
|
dragMode: true
|
||||||
</div>
|
});
|
||||||
<div class="pt-6 mt-6 border-t border-slate-100 flex items-center justify-between">
|
map.pm.setGlobalOptions({ allowSelfIntersection: false });
|
||||||
<a href="http://e8wwc0wgwsws0w4ggoc8g88g.203.24.51.230.sslip.io/" target="_blank" class="px-4 py-2.5 bg-slate-900 hover:bg-slate-800 text-white text-xs font-bold rounded-xl transition-all shadow-sm flex items-center gap-1">
|
|
||||||
Buka Aplikasi
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-white border border-slate-200 rounded-3xl p-6 shadow-sm hover:shadow-xl hover:border-slate-300 transition-all duration-300 flex flex-col justify-between group">
|
let is_edit_atribut_mode = false;
|
||||||
<div class="space-y-4">
|
map.pm.Toolbar.createCustomControl({
|
||||||
<div class="w-12 h-12 bg-emerald-50 border border-emerald-200 text-emerald-600 rounded-2xl flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
|
name: 'EditAtributData',
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="3"/></svg>
|
block: 'custom',
|
||||||
</div>
|
title: 'Edit Nama & Status',
|
||||||
<div class="space-y-1">
|
className: 'ikon-edit-atribut',
|
||||||
<h3 class="font-bold text-lg text-slate-900 tracking-tight">WebGIS PovertyMap</h3>
|
toggle: true,
|
||||||
<p class="text-xs font-medium text-emerald-600 uppercase tracking-wide">Transparansi Sosial & Validasi BPS</p>
|
onClick: () => {
|
||||||
</div>
|
is_edit_atribut_mode = !is_edit_atribut_mode;
|
||||||
<p class="text-xs text-slate-400 font-light leading-relaxed">
|
}
|
||||||
Sistem Informasi Geografis pemetaan klaster kemiskinan masyarakat Kota Pontianak terintegrasi 10 parameter BPS. Menghubungkan titik hunian warga dengan pangkalan rumah ibadah terdekat secara asinkron.
|
});
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="pt-6 mt-6 border-t border-slate-100 flex items-center justify-between">
|
|
||||||
<a href="http://l08g8oso4gk0wc400cggog4s.203.24.51.230.sslip.io/" target="_blank" class="px-4 py-2.5 bg-emerald-600 hover:bg-emerald-700 text-white text-xs font-bold rounded-xl transition-all shadow-sm flex items-center gap-1">
|
|
||||||
Buka Aplikasi
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
// Menentukan gaya warna jalan berdasarkan status
|
||||||
|
function gaya_jalan(status) {
|
||||||
|
if (status === 'Nasional') return { color: 'red', weight: 6 };
|
||||||
|
if (status === 'Provinsi') return { color: 'blue', weight: 4 };
|
||||||
|
return { color: 'green', weight: 2 };
|
||||||
|
}
|
||||||
|
|
||||||
</main>
|
let sedang_memproses = false;
|
||||||
|
|
||||||
<footer class="bg-white border-t border-slate-200 text-center py-6 text-xs font-medium text-slate-400 mt-auto">
|
// Menangani event penggambaran objek spasial baru dari Leaflet-Geoman
|
||||||
© 2026 Jurusan Informatika Universitas Tanjungpura. Dibuat dengan dedikasi penuh.
|
map.on('pm:create', function(e) {
|
||||||
</footer>
|
// Mencegah klik ganda yang memicu pembuatan layer ganda bertumpuk
|
||||||
|
if (sedang_memproses) { map.removeLayer(e.layer); return; }
|
||||||
|
sedang_memproses = true;
|
||||||
|
setTimeout(() => { sedang_memproses = false; }, 500);
|
||||||
|
|
||||||
|
const layer = e.layer;
|
||||||
|
const shape = e.shape;
|
||||||
|
let popup_content = "";
|
||||||
|
|
||||||
|
if (shape === 'Line') {
|
||||||
|
const geojson_mentah = layer.toGeoJSON();
|
||||||
|
// Melakukan escape karakter kutip agar string JSON aman disuntikkan ke atribut onclick HTML
|
||||||
|
const geo_data = JSON.stringify(geojson_mentah.geometry).replace(/"/g, '"');
|
||||||
|
const panjang_meter = turf.length(geojson_mentah, {units: 'kilometers'}) * 1000;
|
||||||
|
|
||||||
|
popup_content = `<div class="form-container"><h4>Tambah Jalan Baru</h4>
|
||||||
|
<input type="text" id="nama_jln" placeholder="Nama Jalan">
|
||||||
|
<select id="stat_jln"><option value="Nasional">Nasional</option><option value="Provinsi">Provinsi</option><option value="Kabupaten">Kabupaten</option></select>
|
||||||
|
<p>Panjang: <b>${panjang_meter.toFixed(2)} m</b></p>
|
||||||
|
<button class="btn-simpan" onclick="simpan_baru('jalan', ${panjang_meter}, '${geo_data}')">Simpan Baru</button></div>`;
|
||||||
|
}
|
||||||
|
else if (shape === 'Polygon') {
|
||||||
|
const geojson_mentah = layer.toGeoJSON();
|
||||||
|
const geo_data = JSON.stringify(geojson_mentah.geometry).replace(/"/g, '"');
|
||||||
|
const luas_meter_p = turf.area(geojson_mentah);
|
||||||
|
|
||||||
|
popup_content = `<div class="form-container"><h4>Tambah Persil Baru</h4>
|
||||||
|
<input type="text" id="no_psl" placeholder="Nomor Persil">
|
||||||
|
<select id="stat_psl"><option value="SHM">SHM</option><option value="HGB">HGB</option><option value="HGU">HGU</option><option value="HP">HP</option></select>
|
||||||
|
<p>Luas: <b>${luas_meter_p.toFixed(2)} m²</b></p>
|
||||||
|
<button class="btn-simpan" onclick="simpan_baru('persil', ${luas_meter_p}, '${geo_data}')">Simpan Baru</button></div>`;
|
||||||
|
layer.setStyle({fillColor: 'yellow', fillOpacity: 0.5, color: 'black'});
|
||||||
|
}
|
||||||
|
else if (shape === 'Marker') {
|
||||||
|
const titik_kordinat = layer.getLatLng();
|
||||||
|
popup_content = `<div class="form-container"><h4>Tambah SPBU Baru</h4>
|
||||||
|
<input type="text" id="nama_spbu" placeholder="Nama SPBU">
|
||||||
|
<input type="text" id="hp_spbu" placeholder="No. HP">
|
||||||
|
<select id="stat_spbu">
|
||||||
|
<option value="24 Jam">Buka 24 Jam</option>
|
||||||
|
<option value="Tidak 24 Jam">Tidak Buka 24 Jam</option>
|
||||||
|
</select>
|
||||||
|
<button class="btn-simpan" onclick="simpan_baru('spbu', 0, null, ${titik_kordinat.lat}, ${titik_kordinat.lng})">Simpan Baru</button></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.bindPopup(popup_content);
|
||||||
|
setTimeout(() => { layer.openPopup(); }, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Menangani event penghapusan data spasial
|
||||||
|
map.on('pm:remove', function(e) {
|
||||||
|
const layer = e.layer;
|
||||||
|
if (layer.db_id && layer.db_type) {
|
||||||
|
if(confirm('Yakin ingin menghapus data ini dari Database permanen?')) {
|
||||||
|
fetch('hapus_feature.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ id: layer.db_id, type: layer.db_type })
|
||||||
|
}).then(r => r.json()).then(res => {
|
||||||
|
if(res.status !== 'success') { alert('Gagal Hapus: ' + res.message); location.reload(); }
|
||||||
|
}).catch(err => {
|
||||||
|
alert('Kesalahan jaringan saat menghapus data.');
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mengumpulkan dan mengirimkan payload data baru ke server PHP
|
||||||
|
window.simpan_baru = function(type, hitungan, geo, lat = null, lng = null) {
|
||||||
|
let payload = { type: type };
|
||||||
|
|
||||||
|
// Membentuk struktur data (payload) berdasarkan tipe objek
|
||||||
|
if (type === 'jalan') {
|
||||||
|
payload.nama = document.getElementById('nama_jln').value;
|
||||||
|
payload.status = document.getElementById('stat_jln').value;
|
||||||
|
payload.besaran = hitungan;
|
||||||
|
payload.geojson = geo;
|
||||||
|
} else if (type === 'persil') {
|
||||||
|
payload.nama = document.getElementById('no_psl').value;
|
||||||
|
payload.status = document.getElementById('stat_psl').value;
|
||||||
|
payload.besaran = hitungan;
|
||||||
|
payload.geojson = geo;
|
||||||
|
} else if (type === 'spbu') {
|
||||||
|
payload.nama = document.getElementById('nama_spbu').value;
|
||||||
|
payload.jam24 = document.getElementById('stat_spbu').value;
|
||||||
|
payload.hp = document.getElementById('hp_spbu').value;
|
||||||
|
payload.lat = lat;
|
||||||
|
payload.lng = lng;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memvalidasi apakah kolom nama sudah diisi sebelum menghubungi server
|
||||||
|
if (!payload.nama || payload.nama.trim() === '') {
|
||||||
|
alert("Informasi nama/identitas wajib diisi!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mengeksekusi permintaan pengiriman data asinkronus ke API backend
|
||||||
|
fetch('simpan_feature.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
})
|
||||||
|
.then(r => r.json()).then(res => {
|
||||||
|
if(res.status === 'success') {
|
||||||
|
alert('Berhasil Disimpan!');
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
alert('Gagal: ' + res.message);
|
||||||
|
console.error(res);
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
alert('Terjadi kesalahan jaringan atau server tidak merespons.');
|
||||||
|
console.error('Catch Error:', err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Memperbarui atribut data yang sudah ada
|
||||||
|
window.update_data = function(type, id) {
|
||||||
|
let nama_baru = document.getElementById('edit_nama').value;
|
||||||
|
if (!nama_baru || nama_baru.trim() === '') {
|
||||||
|
alert("Nama tidak boleh kosong!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload = {
|
||||||
|
type: type,
|
||||||
|
id: id,
|
||||||
|
nama: nama_baru,
|
||||||
|
status: document.getElementById('edit_status').value
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch('edit_feature.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
})
|
||||||
|
.then(r => r.json()).then(res => {
|
||||||
|
if(res.status === 'success') {
|
||||||
|
alert('Berhasil Diupdate!');
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
alert('Gagal Update: ' + res.message);
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
alert('Gagal menghubungi server untuk pembaharuan.');
|
||||||
|
console.error('Catch Error:', err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Merender seluruh data spasial dari pangkalan data ke peta Leaflet
|
||||||
|
function muat_data_feature() {
|
||||||
|
fetch('tampil_feature.php')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(res => {
|
||||||
|
if (res.status === "success") {
|
||||||
|
|
||||||
|
// Merender data garis jalan
|
||||||
|
res.jalan.forEach(j => {
|
||||||
|
const geojson_obj = JSON.parse(j.geojson);
|
||||||
|
L.geoJSON(geojson_obj, {
|
||||||
|
style: gaya_jalan(j.status_jalan),
|
||||||
|
onEachFeature: function(feature, layer) {
|
||||||
|
layer.db_id = j.id; layer.db_type = 'jalan';
|
||||||
|
layer.on('click', function(e) {
|
||||||
|
let html_konten = is_edit_atribut_mode ?
|
||||||
|
`<div class="form-container"><h4>Edit Atribut Jalan</h4>
|
||||||
|
<input type="text" id="edit_nama" value="${j.nama_jalan}">
|
||||||
|
<select id="edit_status">
|
||||||
|
<option value="Nasional" ${j.status_jalan=='Nasional'?'selected':''}>Nasional</option>
|
||||||
|
<option value="Provinsi" ${j.status_jalan=='Provinsi'?'selected':''}>Provinsi</option>
|
||||||
|
<option value="Kabupaten" ${j.status_jalan=='Kabupaten'?'selected':''}>Kabupaten</option>
|
||||||
|
</select>
|
||||||
|
<button class="btn-update" onclick="update_data('jalan', ${j.id})">Simpan Perubahan</button></div>` :
|
||||||
|
`<b>Jalan: ${j.nama_jalan}</b><br>Status: ${j.status_jalan}<br>Panjang: ${j.panjang_m} m`;
|
||||||
|
L.popup().setLatLng(e.latlng).setContent(html_konten).openOn(map);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).addTo(map);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Merender data poligon persil tanah
|
||||||
|
res.persil.forEach(p => {
|
||||||
|
const geojson_obj = JSON.parse(p.geojson);
|
||||||
|
L.geoJSON(geojson_obj, {
|
||||||
|
style: { color: 'orange', weight: 2, fillOpacity: 0.4 },
|
||||||
|
onEachFeature: function(feature, layer) {
|
||||||
|
layer.db_id = p.id; layer.db_type = 'persil';
|
||||||
|
layer.on('click', function(e) {
|
||||||
|
let html_konten = is_edit_atribut_mode ?
|
||||||
|
`<div class="form-container"><h4>Edit Atribut Persil</h4>
|
||||||
|
<input type="text" id="edit_nama" value="${p.nomor_persil}">
|
||||||
|
<select id="edit_status">
|
||||||
|
<option value="SHM" ${p.status_milik=='SHM'?'selected':''}>SHM</option>
|
||||||
|
<option value="HGB" ${p.status_milik=='HGB'?'selected':''}>HGB</option>
|
||||||
|
<option value="HGU" ${p.status_milik=='HGU'?'selected':''}>HGU</option>
|
||||||
|
<option value="HP" ${p.status_milik=='HP'?'selected':''}>HP</option>
|
||||||
|
</select>
|
||||||
|
<button class="btn-update" onclick="update_data('persil', ${p.id})">Simpan Perubahan</button></div>` :
|
||||||
|
`<b>Nomor Persil: ${p.nomor_persil}</b><br>Status: ${p.status_milik}<br>Luas: ${p.luas_m2} m²`;
|
||||||
|
L.popup().setLatLng(e.latlng).setContent(html_konten).openOn(map);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).addTo(map);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Merender titik lokasi SPBU ke dalam grup layer yang berbeda
|
||||||
|
res.spbu.forEach(s => {
|
||||||
|
const spbu_marker = L.marker([parseFloat(s.lat), parseFloat(s.lng)]);
|
||||||
|
spbu_marker.db_id = s.id;
|
||||||
|
spbu_marker.db_type = 'spbu';
|
||||||
|
|
||||||
|
spbu_marker.on('click', function(e) {
|
||||||
|
let html_konten = is_edit_atribut_mode ?
|
||||||
|
`<div class="form-container"><h4>Edit Atribut SPBU</h4>
|
||||||
|
<input type="text" id="edit_nama" value="${s.nama_spbu}">
|
||||||
|
<select id="edit_status">
|
||||||
|
<option value="24 Jam" ${s.is_24jam=='24 Jam'?'selected':''}>Buka 24 Jam</option>
|
||||||
|
<option value="Tidak 24 Jam" ${s.is_24jam=='Tidak 24 Jam'?'selected':''}>Tidak Buka 24 Jam</option>
|
||||||
|
</select>
|
||||||
|
<button class="btn-update" onclick="update_data('spbu', ${s.id})">Simpan Perubahan</button></div>` :
|
||||||
|
`<b>SPBU: ${s.nama_spbu}</b><br>Status: ${s.is_24jam}<br>No. HP: ${s.no_hp}`;
|
||||||
|
L.popup().setLatLng(e.latlng).setContent(html_konten).openOn(map);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (s.is_24jam === '24 Jam') {
|
||||||
|
spbu_marker.addTo(layer_spbu_24);
|
||||||
|
} else {
|
||||||
|
spbu_marker.addTo(layer_spbu_non_24);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
layer_spbu_24.addTo(map);
|
||||||
|
layer_spbu_non_24.addTo(map);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
alert("Gagal memuat data peta: " + res.message);
|
||||||
|
console.error("Kesalahan Sistem:", res.message);
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('Kesalahan Jaringan: Gagal merender data peladen', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
muat_data_feature();
|
||||||
|
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user