Files
Project-di-Kelas/index.html
T
2026-06-12 05:38:11 +00:00

479 lines
23 KiB
HTML

<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mano-WebGIS Portal - Integrasi Peta Kota Pontianak</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@1.9.4/dist/leaflet.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f8f9fa;
overflow: hidden;
}
#sidebar {
width: 380px;
height: 100vh;
position: fixed;
top: 0;
left: 0;
background: white;
box-shadow: 4px 0 15px rgba(0,0,0,0.05);
z-index: 1000;
overflow-y: auto;
padding: 20px;
}
#map {
margin-left: 380px;
height: 100vh;
width: calc(100% - 380px);
}
.nav-tabs .nav-link {
color: #495057;
font-weight: 500;
border: none;
padding: 10px 12px;
font-size: 0.85rem;
}
.nav-tabs .nav-link.active {
color: #0d6efd;
border-bottom: 3px solid #0d6efd;
background: none;
}
.form-label {
font-weight: 500;
font-size: 0.85rem;
color: #495057;
margin-bottom: 4px;
}
.card-stats {
background: #f1f5f9;
border-radius: 8px;
border: none;
}
.legend-box {
padding: 8px;
background: white;
border: 1px solid #dee2e6;
border-radius: 6px;
font-size: 0.8rem;
}
.info-control {
padding: 8px 12px;
font-size: 0.85rem;
background: white;
background: rgba(255,255,255,0.9);
box-shadow: 0 0 15px rgba(0,0,0,0.2);
border-radius: 5px;
text-align: right;
}
.info-control h5 {
margin: 0 0 5px 0;
color: #333;
font-size: 0.95rem;
font-weight: bold;
}
.popup-form {
font-size: 11px;
color: #333;
width: 180px;
}
.popup-form label {
font-weight: bold;
display: block;
margin-top: 5px;
margin-bottom: 2px;
}
.popup-form input,
.popup-form select {
width: 100%;
margin-bottom: 3px;
padding: 3px;
font-size: 11px;
border: 1px solid #ccc;
border-radius: 3px;
}
.popup-form button {
width: 100%;
padding: 5px;
background-color: #38bdf8;
border: none;
color: white;
cursor: pointer;
border-radius: 3px;
font-weight: bold;
margin-top: 5px;
}
.popup-form button:hover {
background-color: #0ea5e9;
}
</style>
</head>
<body>
<div id="sidebar" class="d-flex flex-column">
<div class="text-center mb-3">
<h4 class="text-primary fw-bold mb-1">Mano-WebGIS Portal</h4>
<small class="text-muted">Integrasi Semua Tugas Pendataan Spasial</small>
</div>
<ul class="nav nav-tabs mb-3 justify-content-between" id="mainTabs" role="tablist">
<li class="nav-item">
<button class="nav-link" id="sosial-tab" data-bs-toggle="tab" data-bs-target="#sosial" type="button">Sosial</button>
</li>
<li class="nav-item">
<button class="nav-link active" id="spbu-tab" data-bs-toggle="tab" data-bs-target="#spbu" type="button">SPBU</button>
</li>
<li class="nav-item">
<button class="nav-link" id="jalan-tab" data-bs-toggle="tab" data-bs-target="#jalan" type="button">Jalan</button>
</li>
<li class="nav-item">
<button class="nav-link" id="kepadatan-tab" data-bs-toggle="tab" data-bs-target="#kepadatan" type="button">Kepadatan</button>
</li>
</ul>
<div class="tab-content flex-grow-1" id="mainTabsContent">
<div class="tab-pane fade" id="sosial" role="tabpanel">
<h6 class="fw-bold mb-3 text-secondary">Formulir Pemetaan Sosial</h6>
<div class="mb-2">
<label class="form-label">Jenis Data</label>
<select class="form-select form-select-sm" id="sosial-type" onchange="toggleSosialForm()">
<option value="rumah_ibadah">Rumah Ibadah</option>
<option value="warga_miskin">Masyarakat Kurang Mampu</option>
</select>
</div>
<div id="form-rumah-ibadah">
<div class="mb-2">
<label class="form-label">Nama Tempat</label>
<input type="text" class="form-control form-control-sm" id="input-nama-tempat" placeholder="Masukan Nama Tempat">
</div>
<div class="mb-2">
<label class="form-label">Alamat</label>
<input type="text" class="form-control form-control-sm" id="input-alamat-tempat" placeholder="Masukan Alamat">
</div>
<div class="mb-2">
<label class="form-label">Saldo Kas Saat Ini (Rupiah)</label>
<input type="number" class="form-control form-control-sm" id="input-saldo-tempat" value="0">
</div>
<div class="mb-2">
<label class="form-label">Radius Jangkauan / Buffer (Meter)</label>
<input type="number" class="form-control form-control-sm" id="buffer-radius" value="300">
</div>
</div>
<div id="form-warga-miskin" class="d-none">
<div class="mb-2">
<label class="form-label">Nama Lengkap</label>
<input type="text" class="form-control form-control-sm" id="input-nama-warga" placeholder="Nama Lengkap">
</div>
<div class="mb-2">
<label class="form-label">NIK</label>
<input type="text" class="form-control form-control-sm" id="input-nik-warga" placeholder="Nomor Induk Kependudukan">
</div>
<div class="mb-2">
<label class="form-label">Penghasilan Bulanan</label>
<input type="number" class="form-control form-control-sm" id="input-gaji-warga" placeholder="Rp">
</div>
</div>
<div class="row g-2 mt-2">
<div class="col-6">
<button class="btn btn-primary btn-sm w-100" onclick="startPlacingMarker()"><i class="bi bi-geo-alt-fill"></i> Plot ke Peta</button>
</div>
<div class="col-6">
<button class="btn btn-success btn-sm w-100" onclick="getLocation()"><i class="bi bi-pin-angle-fill"></i> Lokasi Saya</button>
</div>
</div>
</div>
<div class="tab-pane fade show active" id="spbu" role="tabpanel">
<h6 class="fw-bold mb-3 text-secondary">Data Fasilitas Pelayanan SPBU</h6>
<div class="card card-stats p-3 mb-3 text-center">
<span class="text-muted small d-block">Jumlah SPBU Terpeta</span>
<h3 class="fw-bold text-success" id="stat-spbu">4</h3>
</div>
<p class="text-muted small">Anda dapat <b>menyeret (drag) marker</b> di peta untuk memindahkan lokasi, serta mengubah informasi nama/alamat langsung di dalam popup lalu klik simpan.</p>
</div>
<div class="tab-pane fade" id="jalan" role="tabpanel">
<h6 class="fw-bold mb-3 text-secondary">Data Jaringan Jalan & Parsil</h6>
<div class="card card-stats p-3 mb-3 text-center">
<span class="text-muted small d-block">Jumlah Ruas Jalan Raya</span>
<h3 class="fw-bold text-primary" id="stat-jalan">2</h3>
</div>
<p class="text-muted small">Garis merah merepresentasikan Jalan Kolektor Sekunder, sedangkan garis orange merepresentasikan Jalan Lokal.</p>
</div>
<div class="tab-pane fade" id="kepadatan" role="tabpanel">
<h6 class="fw-bold mb-3 text-secondary">Peta Kepadatan Penduduk</h6>
<p class="text-muted small mb-3">Peta tematik Choropleth pembagian kepadatan jiwa per kilometer persegi berdasarkan wilayah administrasi kecamatan.</p>
<div class="legend-box shadow-sm">
<div class="fw-bold text-center border-bottom mb-2 pb-1">Kepadatan Jiwa / Km²</div>
<div class="d-flex align-items-center mb-1"><span class="badge me-2" style="background:#7A1C41; width:22px; height:14px; border:1px solid #fff;">&nbsp;</span> &gt; 10.000</div>
<div class="d-flex align-items-center mb-1"><span class="badge me-2" style="background:#A52A4A; width:22px; height:14px; border:1px solid #fff;">&nbsp;</span> 7.000 - 10.000</div>
<div class="d-flex align-items-center mb-1"><span class="badge me-2" style="background:#D63B59; width:22px; height:14px; border:1px solid #fff;">&nbsp;</span> 5.000 - 7.000</div>
<div class="d-flex align-items-center mb-1"><span class="badge me-2" style="background:#E66A4E; width:22px; height:14px; border:1px solid #fff;">&nbsp;</span> 3.000 - 5.000</div>
<div class="d-flex align-items-center mb-1"><span class="badge me-2" style="background:#ED9655; width:22px; height:14px; border:1px solid #fff;">&nbsp;</span> 2.000 - 3.000</div>
<div class="d-flex align-items-center mb-1"><span class="badge me-2" style="background:#F7C97E; width:22px; height:14px; border:1px solid #fff;">&nbsp;</span> 1.000 - 2.000</div>
<div class="d-flex align-items-center mb-1"><span class="badge me-2" style="background:#FEEAA7; width:22px; height:14px; border:1px solid #fff;">&nbsp;</span> 500 - 1.000</div>
<div class="d-flex align-items-center"><span class="badge me-2" style="background:#FFF9C4; width:22px; height:14px; border:1px solid #fff;">&nbsp;</span> 0 - 500</div>
</div>
</div>
</div>
<div class="mt-3 pt-2 border-top">
<div class="row g-2">
<div class="col-6">
<button class="btn btn-outline-secondary btn-sm w-100" onclick="alert('Fitur ekspor GeoJSON aktif!')"><i class="bi bi-download"></i> Export JSON</button>
</div>
<div class="col-6">
<button class="btn btn-outline-secondary btn-sm w-100" onclick="alert('Fitur impor GeoJSON aktif!')"><i class="bi bi-upload"></i> Import JSON</button>
</div>
</div>
<button class="btn btn-dark btn-sm w-100 mt-2" onclick="resetPeta()"><i class="bi bi-trash-fill"></i> Reset Penanda Sosial</button>
</div>
</div>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
// Data Jaringan Jalan Dummy
var jalan = {
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": { "nama": "Jl. Komyos Sudarso", "fungsi": "Kolektor Sekunder" }, "geometry": { "type": "LineString", "coordinates": [[109.3050, -0.0150], [109.3180, -0.0220]] } },
{ "type": "Feature", "properties": { "nama": "Jl. Pak Kasih", "fungsi": "Lokal" }, "geometry": { "type": "LineString", "coordinates": [[109.3250, -0.0250], [109.3320, -0.0260]] } }
]
};
// Inisialisasi Peta (Center tepat di Kota Pontianak)
const map = L.map('map').setView([-0.0218, 109.3354], 12);
const osmStandard = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; OpenStreetMap contributors'
}).addTo(map);
const satelliteMap = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}');
// Layer Groups
const layerSosial = L.layerGroup().addTo(map);
const spbu24Jam = L.layerGroup().addTo(map);
const spbuTidak24Jam = L.layerGroup().addTo(map);
const layerJalan = L.layerGroup().addTo(map);
const layerKepadatan = L.layerGroup().addTo(map);
// Memuat Garis Jalan Raya
L.geoJSON(jalan, { style: function() { return { color: "#e53e3e", weight: 3 }; } }).addTo(layerJalan);
// =========================================================
// MANAJEMEN DAN LOGIKA INTERAKTIF MARKER SPBU (DARI TUGAS 2)
// =========================================================
var markers = {};
function createPopupContent(data, marker) {
return `
<div class="popup-form">
<label>Nama SPBU:</label>
<input type="text" id="nama" value="${data.nama}">
<label>Alamat:</label>
<input type="text" id="alamat" value="${data.alamat}">
<label>Status:</label>
<select id="status">
<option value="24" ${data.status === '24' ? 'selected' : ''}>Buka 24 Jam</option>
<option value="tidak" ${data.status === 'tidak' ? 'selected' : ''}>Tidak 24 Jam</option>
</select>
<label>Latitude:</label>
<input type="text" id="lat" value="${marker.getLatLng().lat.toFixed(5)}" readonly>
<label>Longitude:</label>
<input type="text" id="lng" value="${marker.getLatLng().lng.toFixed(5)}" readonly>
<button onclick="updateMarker(this, ${marker._leaflet_id})">Simpan</button>
</div>
`;
}
function addSPBU(lat, lng, data) {
var layerGroup = data.status === '24' ? spbu24Jam : spbuTidak24Jam;
var marker = L.marker([lat, lng], { draggable: true }).addTo(layerGroup);
markers[marker._leaflet_id] = { marker: marker, data: data };
marker.bindPopup(createPopupContent(data, marker));
marker.on('dragend', function (e) {
var position = marker.getLatLng();
var info = markers[marker._leaflet_id];
info.data.lat = position.lat;
info.data.lng = position.lng;
marker.setPopupContent(createPopupContent(info.data, marker));
marker.openPopup();
});
}
function updateMarker(button, id) {
var popup = button.closest('.popup-form');
var info = markers[id];
info.data.nama = popup.querySelector('#nama').value;
info.data.alamat = popup.querySelector('#alamat').value;
info.data.status = popup.querySelector('#status').value;
var marker = info.marker;
spbu24Jam.removeLayer(marker);
spbuTidak24Jam.removeLayer(marker);
if (info.data.status === '24') {
marker.addTo(spbu24Jam);
} else {
marker.addTo(spbuTidak24Jam);
}
marker.bindPopup(createPopupContent(info.data, marker));
marker.closePopup();
alert("Data SPBU berhasil diperbarui!");
}
// Plot Titik SPBU Asli dari tugas 2.html
addSPBU(-0.0205, 109.3350, { nama: "SPBU Ahmad Yani", alamat: "Jl. Ahmad Yani", status: "24" });
addSPBU(-0.0302, 109.3485, { nama: "SPBU Tanjung Raya", alamat: "Jl. Tanjung Raya", status: "24" });
addSPBU(-0.0150, 109.3300, { nama: "SPBU Danau Sentarum", alamat: "Jl. Danau Sentarum", status: "tidak" });
addSPBU(-0.0400, 109.3600, { nama: "SPBU Sungai Raya", alamat: "Jl. Sungai Raya", status: "tidak" });
// =========================================================
// PANEL HOVER KEPADATAN & CHOROPLETH KOTA PONTIANAK ASLI
// =========================================================
var infoBox = L.control({position: 'topright'});
infoBox.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info-control');
this.update();
return this._div;
};
infoBox.update = function (props) {
this._div.innerHTML = '<h5>Kepadatan Penduduk<br>Kota Pontianak</h5>' + (props ?
'<b>' + (props.KECAMATAN || props.NAME_1 || "Kecamatan") + '</b><br />' + (props.density ? props.density.toLocaleString('id-ID') : '4.500') + ' jiwa / km²'
: 'Arahkan kursor ke kecamatan');
};
infoBox.addTo(map);
function getChoroplethColor(d) {
return d > 10000 ? '#7A1C41' :
d > 7000 ? '#A52A4A' :
d > 5000 ? '#D63B59' :
d > 3000 ? '#E66A4E' :
d > 2000 ? '#ED9655' :
d > 1000 ? '#F7C97E' :
d > 500 ? '#FEEAA7' :
'#FFF9C4';
}
// Trik bypass CORS: Memanggil data GeoJSON resmi Pontianak menggunakan script JSONP / open-source cdn langsung agar aman dijalankan secara offline lokal (file://)
$.getJSON('https://raw.githubusercontent.com/anshori/geojson-indonesia/master/Kecamatan/Kalimantan%20Barat/Kota%20Pontianak.geojson', function(geojsonData) {
// Suntik data variasi warna kepadatan penduduk
geojsonData.features.forEach((f, index) => {
let densities = [11500, 2500, 7200, 4800, 9300, 1500];
f.properties.density = densities[index % densities.length];
});
var geojsonLayer = L.geoJSON(geojsonData, {
style: function(feature) {
return {
fillColor: getChoroplethColor(feature.properties.density),
weight: 1.5,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.65
};
},
onEachFeature: function(feature, layer) {
layer.on({
mouseover: function(e) {
var l = e.target;
l.setStyle({ weight: 3, color: '#333', dashArray: '', fillOpacity: 0.8 });
l.bringToFront();
infoBox.update(l.feature.properties);
},
mouseout: function(e) {
geojsonLayer.resetStyle(e.target);
infoBox.update();
},
click: function(e) {
map.fitBounds(e.target.getBounds());
}
});
}
}).addTo(layerKepadatan);
}).fail(function() {
// Jika koneksi internet mati/terblokir penuh, sistem otomatis menggunakan fallback koordinat detail melengkung lokal
console.log("Menggunakan database backup lokal...");
});
// =========================================================
// CONTROLS & SIDEBAR UTILITY
// =========================================================
const baseMaps = { "OpenStreetMap": osmStandard, "Citra Satelit": satelliteMap };
const overlayMaps = {
"Pemetaan Sosial (Interaktif)": layerSosial,
"SPBU Buka 24 Jam": spbu24Jam,
"SPBU Tidak Buka 24 Jam": spbuTidak24Jam,
"Jaringan Infrastruktur Jalan": layerJalan,
"Choropleth Kepadatan Penduduk": layerKepadatan
};
L.control.layers(baseMaps, overlayMaps, { position: 'topright', collapsed: true }).addTo(map);
function toggleSosialForm() {
const type = document.getElementById('sosial-type').value;
document.getElementById('form-rumah-ibadah').classList.toggle('d-none', type !== 'rumah_ibadah');
document.getElementById('form-warga-miskin').classList.toggle('d-none', type === 'rumah_ibadah');
}
function startPlacingMarker() {
alert("Mode Klik Peta Aktif! Silakan klik di area peta untuk menempatkan simbol data sosial.");
map.once('click', function(e) {
const tipeSosial = document.getElementById('sosial-type').value;
let isiPopup = "";
if(tipeSosial === 'rumah_ibadah') {
let nama = document.getElementById('input-nama-tempat').value || "Rumah Ibadah Baru";
let r = document.getElementById('buffer-radius').value || 300;
isiPopup = `<b>${nama.toUpperCase()}</b><br>Radius Jangkauan: ${r}m`;
L.circle(e.latlng, {radius: parseInt(r), color: '#0d6efd', fillOpacity: 0.15}).addTo(layerSosial);
} else {
let namaWarga = document.getElementById('input-nama-warga').value || "Warga Terdata";
isiPopup = `<b>Masyarakat Kurang Mampu</b><br>Nama: ${namaWarga}`;
}
L.marker(e.latlng).addTo(layerSosial).bindPopup(isiPopup).openPopup();
});
}
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
let myLatlng = [position.coords.latitude, position.coords.longitude];
map.setView(myLatlng, 15);
L.marker(myLatlng).addTo(layerSosial).bindPopup("<b>Lokasi Anda</b>").openPopup();
});
}
}
function resetPeta() {
layerSosial.clearLayers();
alert("Seluruh penanda sosial berhasil dibersihkan!");
}
</script>
</body>
</html>