143 lines
6.5 KiB
HTML
143 lines
6.5 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="id" data-theme="dark">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>WebGIS — Pertemuan 01: Geometri Dasar</title>
|
|
<meta name="description" content="Modul 01 — Menggambar dan mengelola geometri spasial (titik, garis, poligon) berbasis WebGIS.">
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css" />
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
|
<link rel="stylesheet" href="css/main.css">
|
|
<script>
|
|
// Global error interceptor to assist in visual debugging
|
|
window.onerror = function(message, source, lineno, colno, error) {
|
|
const errorMsg = `JS Error: ${message} (at ${source ? source.split('/').pop() : 'unknown'}:${lineno}:${colno})`;
|
|
console.error(errorMsg, error);
|
|
const showInterval = setInterval(() => {
|
|
const toastContainer = document.getElementById('toast-container');
|
|
if (toastContainer) {
|
|
clearInterval(showInterval);
|
|
const t = document.createElement('div');
|
|
t.className = 'toast error';
|
|
t.innerHTML = errorMsg;
|
|
toastContainer.appendChild(t);
|
|
}
|
|
}, 100);
|
|
return false;
|
|
};
|
|
window.onunhandledrejection = function(event) {
|
|
const errorMsg = `Promise Error: ${event.reason?.message || event.reason}`;
|
|
console.error(errorMsg, event.reason);
|
|
const showInterval = setInterval(() => {
|
|
const toastContainer = document.getElementById('toast-container');
|
|
if (toastContainer) {
|
|
clearInterval(showInterval);
|
|
const t = document.createElement('div');
|
|
t.className = 'toast error';
|
|
t.innerHTML = errorMsg;
|
|
toastContainer.appendChild(t);
|
|
}
|
|
}, 100);
|
|
};
|
|
</script>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- Map container -->
|
|
<div id="map"></div>
|
|
|
|
<!-- Floating Dock -->
|
|
<div class="floating-dock">
|
|
|
|
<!-- Dock top bar: back + theme toggle -->
|
|
<div class="dock-topbar">
|
|
<a href="../../portal.html" class="btn-back-portal" title="Kembali ke Portal">
|
|
<i class="fas fa-chevron-left"></i>
|
|
Portal
|
|
</a>
|
|
<div class="dock-controls">
|
|
<button class="btn-theme-dock" id="themeToggle" title="Toggle tema" aria-label="Toggle dark/light mode">
|
|
<i class="fas fa-moon" id="themeIcon"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Header -->
|
|
<div class="header">
|
|
<h2>P01: Geometri Dasar</h2>
|
|
<p>Sistem Informasi Geografis — CRUD spasial titik, garis, dan area.</p>
|
|
</div>
|
|
|
|
<!-- Layer Peta (Centang) -->
|
|
<div class="menu-group">
|
|
<div class="menu-title">Layer Peta</div>
|
|
<div class="form-panel" style="gap: 10px;">
|
|
<label class="checkbox-group">
|
|
<input type="checkbox" id="layer-spbu" checked>
|
|
<span>Stasiun SPBU</span>
|
|
</label>
|
|
<label class="checkbox-group">
|
|
<input type="checkbox" id="layer-jalan" checked>
|
|
<span>Jalur Jalan</span>
|
|
</label>
|
|
<label class="checkbox-group">
|
|
<input type="checkbox" id="layer-kavling" checked>
|
|
<span>Kavling Tanah</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Spatial tools -->
|
|
<div class="menu-group">
|
|
<div class="menu-title">Alat Spasial</div>
|
|
|
|
<button id="btn-draw-spbu" class="btn-action btn-spbu">
|
|
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.242-4.243a8 8 0 1111.314 0z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path></svg>
|
|
Tambah SPBU
|
|
</button>
|
|
|
|
<button id="btn-draw-jalan" class="btn-action btn-jalan">
|
|
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path></svg>
|
|
Tambah Jalan
|
|
</button>
|
|
|
|
<button id="btn-draw-kavling" class="btn-action btn-kavling">
|
|
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"></path></svg>
|
|
Tambah Kavling
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Dynamic form container -->
|
|
<div id="form-container" style="display: none;"></div>
|
|
|
|
</div>
|
|
|
|
<!-- Toast -->
|
|
<div id="toast-container" class="toast-container"></div>
|
|
|
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
|
|
<script>
|
|
// ── Theme toggle ──
|
|
const html = document.documentElement;
|
|
const btn = document.getElementById('themeToggle');
|
|
const icon = document.getElementById('themeIcon');
|
|
const saved = localStorage.getItem('portal-theme') || 'dark';
|
|
setTheme(saved);
|
|
btn.addEventListener('click', () => {
|
|
const next = html.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
|
|
setTheme(next);
|
|
localStorage.setItem('portal-theme', next);
|
|
});
|
|
function setTheme(t) {
|
|
html.setAttribute('data-theme', t);
|
|
icon.className = t === 'dark' ? 'fas fa-sun' : 'fas fa-moon';
|
|
}
|
|
</script>
|
|
<script type="module" src="js/app.js?v=20260613-9"></script>
|
|
|
|
</body>
|
|
</html>
|