Files
WebGIS_Project/03/choropleth-pontianak (2).html
T
luthfihadinugroho79 e950468760 add files
2026-06-10 19:42:59 +07:00

284 lines
10 KiB
HTML

<!DOCTYPE html>
<html lang="id">
<head>
<base target="_top">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pontianak Dashboard - Satellite Mode</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
:root {
--glass-bg: rgba(15, 23, 42, 0.8);
--glass-border: rgba(255, 255, 255, 0.1);
--accent-blue: #38bdf8;
--accent-orange: #fb923c;
}
html, body { height: 100%; margin: 0; padding: 0; font-family: 'Inter', sans-serif; background: #020617; color: white; }
#map { width: 100%; height: 100vh; }
/* Sidebar Statistik */
.stats-sidebar {
position: absolute;
left: 20px;
top: 20px;
bottom: 20px;
width: 320px;
z-index: 1000;
display: flex;
flex-direction: column;
gap: 15px;
pointer-events: none; /* Supaya klik bisa tembus ke peta kecuali di dalam elemen ini */
}
.stats-card {
background: var(--glass-bg);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid var(--glass-border);
border-radius: 16px;
padding: 20px;
pointer-events: auto;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3);
}
.stats-card h2 { margin: 0 0 5px; font-size: 18px; font-weight: 700; color: white; }
.stats-card p { margin: 0; font-size: 13px; color: #94a3b8; }
.stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 15px; }
.stat-item { background: rgba(255,255,255,0.05); padding: 12px; border-radius: 10px; }
.stat-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px; color: #64748b; }
.stat-value { font-size: 15px; font-weight: 600; margin-top: 2px; }
/* Peringkat List */
.rank-list { margin-top: 15px; list-style: none; padding: 0; }
.rank-item { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.05); font-size: 13px; }
.rank-item:last-child { border: none; }
.rank-name { color: #cbd5e1; }
.rank-val { font-weight: 600; color: var(--accent-blue); }
/* Info Hover Panel */
#hover-info { border-left: 4px solid var(--accent-blue); }
.hover-name { font-size: 20px; font-weight: 700; color: white; margin-bottom: 4px; }
.hover-data { font-size: 16px; color: var(--accent-blue); font-weight: 600; }
/* Control buttons */
.layer-toggle {
position: absolute;
right: 20px;
top: 20px;
background: var(--glass-bg);
backdrop-filter: blur(8px);
padding: 5px;
border-radius: 12px;
border: 1px solid var(--glass-border);
display: flex;
gap: 5px;
z-index: 1000;
}
.btn-mode {
background: transparent;
border: none;
color: #94a3b8;
padding: 8px 15px;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
font-size: 12px;
transition: 0.2s;
}
.btn-mode.active { background: white; color: #0f172a; }
/* Legend */
.legend {
padding: 15px;
color: white;
line-height: 22px;
font-size: 12px;
}
.legend i { width: 14px; height: 14px; float: left; margin-right: 8px; margin-top: 4px; border-radius: 3px; }
</style>
</head>
<body>
<div class="stats-sidebar">
<div class="stats-card">
<h2>Kota Pontianak</h2>
<p>Ringkasan Data Wilayah</p>
<div class="stat-grid">
<div class="stat-item">
<div class="stat-label">Total Populasi</div>
<div class="stat-value" id="total-pop">-</div>
</div>
<div class="stat-item">
<div class="stat-label">Avg. Kepadatan</div>
<div class="stat-value" id="avg-dens">-</div>
</div>
</div>
<div style="margin-top: 20px;">
<div class="stat-label">Peringkat Kecamatan</div>
<div id="rank-container" class="rank-list"></div>
</div>
</div>
<div class="stats-card" id="hover-info">
<div id="hover-content">
<div class="stat-label">Detail Kecamatan</div>
<div style="color:#64748b; margin-top:10px; font-style:italic;">Arahkan kursor ke peta...</div>
</div>
</div>
</div>
<div class="layer-toggle">
<button class="btn-mode active" id="btn-density" onclick="setMode('density')">Kepadatan</button>
<button class="btn-mode" id="btn-population" onclick="setMode('population')">Populasi</button>
</div>
<div id="map"></div>
<script type="text/javascript" src="pontianak-state.js"></script>
<script>
// ── Inisialisasi Peta ───────────────────────────────────────────────────
const map = L.map('map', { zoomControl: false }).setView([-0.023868, 109.338493], 12);
// LAYER SATELIT (Esri World Imagery)
const satellite = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EBP, and the GIS User Community'
}).addTo(map);
// Tambahan Label Jalan (Transparan) agar lokasi mudah dikenali di satelit
L.tileLayer('https://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}{r}.png', {
pane: 'shadowPane', // Menaruh label di atas layer data
pointerEvents: 'none'
}).addTo(map);
let currentMode = 'density';
const modeConfig = {
density: {
unit: 'jiwa/km²',
grades: [0, 1000, 3000, 5000, 7000, 10000],
getColor: d => d > 10000 ? '#7f1d1d' : d > 7000 ? '#b91c1c' : d > 5000 ? '#ef4444' : d > 3000 ? '#f87171' : d > 1000 ? '#fca5a5' : '#fee2e2'
},
population: {
unit: 'jiwa',
grades: [0, 40000, 70000, 100000, 130000],
getColor: d => d > 130000 ? '#0c4a6e' : d > 100000 ? '#0284c7' : d > 70000 ? '#38bdf8' : d > 40000 ? '#bae6fd' : '#f0f9ff'
}
};
// ── Kalkulasi Statistik Global ──────────────────────────────────────────
function calculateStats() {
const features = statesData.features;
const totalPop = features.reduce((sum, f) => sum + f.properties.population, 0);
const avgDens = features.reduce((sum, f) => sum + f.properties.density, 0) / features.length;
document.getElementById('total-pop').innerText = totalPop.toLocaleString('id-ID');
document.getElementById('avg-dens').innerText = Math.round(avgDens).toLocaleString('id-ID');
updateRanking();
}
function updateRanking() {
const container = document.getElementById('rank-container');
const sorted = [...statesData.features].sort((a, b) =>
b.properties[currentMode] - a.properties[currentMode]
).slice(0, 5); // Ambil Top 5
container.innerHTML = sorted.map(f => `
<div class="rank-item">
<span class="rank-name">${f.properties.name}</span>
<span class="rank-val">${f.properties[currentMode].toLocaleString('id-ID')}</span>
</div>
`).join('');
}
// ── Map Events ──────────────────────────────────────────────────────────
function getStyle(feature) {
return {
weight: 1.5,
opacity: 1,
color: 'rgba(255,255,255,0.4)',
fillOpacity: 0.7,
fillColor: modeConfig[currentMode].getColor(feature.properties[currentMode])
};
}
function highlightFeature(e) {
const layer = e.target;
const props = layer.feature.properties;
layer.setStyle({ weight: 3, color: 'white', fillOpacity: 0.9 });
layer.bringToFront();
// Update panel kiri bawah
const accent = currentMode === 'density' ? 'var(--accent-orange)' : 'var(--accent-blue)';
document.getElementById('hover-content').innerHTML = `
<div class="stat-label">Detail Kecamatan</div>
<div class="hover-name">${props.name}</div>
<div class="hover-data" style="color: ${accent}">${props[currentMode].toLocaleString('id-ID')} ${modeConfig[currentMode].unit}</div>
<div style="font-size:12px; color:#94a3b8; margin-top:5px;">
${currentMode === 'density' ? 'Populasi: ' + props.population.toLocaleString('id-ID') : 'Kepadatan: ' + props.density.toLocaleString('id-ID')}
</div>
`;
}
function resetHighlight(e) {
geojson.resetStyle(e.target);
document.getElementById('hover-content').innerHTML = `
<div class="stat-label">Detail Kecamatan</div>
<div style="color:#64748b; margin-top:10px; font-style:italic;">Arahkan kursor ke peta...</div>
`;
}
const geojson = L.geoJson(statesData, {
style: getStyle,
onEachFeature: (f, l) => {
l.on({ mouseover: highlightFeature, mouseout: resetHighlight, click: (e) => map.fitBounds(e.target.getBounds()) });
}
}).addTo(map);
// ── Legend ──────────────────────────────────────────────────────────────
const legend = L.control({ position: 'bottomright' });
legend.onAdd = function () {
this._div = L.DomUtil.create('div', 'legend stats-card');
this.update();
return this._div;
};
legend.update = function () {
const cfg = modeConfig[currentMode];
let html = `<div class="stat-label" style="margin-bottom:10px">${cfg.unit}</div>`;
for (let i = 0; i < cfg.grades.length; i++) {
html += `<i style="background:${cfg.getColor(cfg.grades[i] + 1)}"></i> ` +
cfg.grades[i].toLocaleString('id-ID') + (cfg.grades[i+1] ? `&ndash;${cfg.grades[i+1].toLocaleString('id-ID')}<br>` : '+');
}
this._div.innerHTML = html;
};
legend.addTo(map);
// ── Switch Mode ─────────────────────────────────────────────────────────
function setMode(mode) {
currentMode = mode;
document.getElementById('btn-density').classList.toggle('active', mode === 'density');
document.getElementById('btn-population').classList.toggle('active', mode === 'population');
geojson.setStyle(getStyle);
legend.update();
updateRanking();
}
// Jalankan kalkulasi pertama kali
calculateStats();
</script>
</body>
</html>