1111 lines
50 KiB
PHP
1111 lines
50 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="id">
|
|
<head>
|
|
<base target="_top">
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>WebGIS SPBU & Parsil Tanah</title>
|
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" crossorigin=""/>
|
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" crossorigin=""></script>
|
|
|
|
<link rel="stylesheet" href="https://unpkg.com/@geoman-io/leaflet-geoman-free@latest/dist/leaflet-geoman.css" />
|
|
<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>
|
|
/* ─── DESIGN TOKENS ─── */
|
|
:root {
|
|
--blue: #2563EB;
|
|
--blue-d: #1D4ED8;
|
|
--blue-l: #EFF6FF;
|
|
--navy: #0F172A;
|
|
--green: #10B981;
|
|
--green-d: #059669;
|
|
--green-l: #ECFDF5;
|
|
--red: #EF4444;
|
|
--amber: #F59E0B;
|
|
--surface: #FFFFFF;
|
|
--border: #E2E8F0;
|
|
--border-l:#F1F5F9;
|
|
--text-2: #475569;
|
|
--text-3: #94A3B8;
|
|
--r: 16px;
|
|
--r2: 12px;
|
|
--r3: 8px;
|
|
}
|
|
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
html, body { height: 100%; font-family: 'Plus Jakarta Sans', sans-serif; overflow: hidden; background: #f8fafc; }
|
|
|
|
/* ── NAVBAR ── */
|
|
#navbar {
|
|
position: fixed;
|
|
top: 0; left: 0; right: 0;
|
|
height: 56px;
|
|
z-index: 1200;
|
|
background: rgba(255,255,255,0.92);
|
|
backdrop-filter: blur(24px);
|
|
border-bottom: 1px solid rgba(226,232,240,0.7);
|
|
box-shadow: 0 1px 16px rgba(15,23,42,0.08);
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 16px;
|
|
}
|
|
.nav-brand {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 9px;
|
|
text-decoration: none;
|
|
}
|
|
.nav-brand-logo {
|
|
width: 32px; height: 32px;
|
|
border-radius: 9px;
|
|
background: linear-gradient(135deg, #10B981, #059669);
|
|
display: flex; align-items: center; justify-content: center;
|
|
box-shadow: 0 2px 8px rgba(16,185,129,0.35);
|
|
}
|
|
.nav-brand-text {
|
|
font-size: 14px;
|
|
font-weight: 800;
|
|
color: var(--navy);
|
|
letter-spacing: -0.3px;
|
|
}
|
|
|
|
/* ── MAP ── */
|
|
#map {
|
|
position: fixed;
|
|
top: 56px;
|
|
left: 0; right: 0; bottom: 0;
|
|
z-index: 0;
|
|
}
|
|
|
|
/* ── MODERN POPUP (LEAFLET OVERRIDE) ── */
|
|
.leaflet-popup-content-wrapper {
|
|
border-radius: var(--r) !important;
|
|
border: none !important;
|
|
box-shadow: 0 16px 48px rgba(15,23,42,0.2) !important;
|
|
font-family: 'Plus Jakarta Sans',sans-serif !important;
|
|
padding: 0 !important;
|
|
overflow: hidden !important;
|
|
}
|
|
.leaflet-popup-content {
|
|
margin: 0 !important;
|
|
width: 260px !important;
|
|
display: flex !important;
|
|
flex-direction: column !important;
|
|
}
|
|
|
|
.lp-hd {
|
|
padding: 14px 16px 10px;
|
|
border-bottom: 1px solid var(--border-l);
|
|
background: linear-gradient(to bottom, #f8fafc, white);
|
|
}
|
|
.lp-ti { font-size: 14px; font-weight: 800; color: var(--navy); }
|
|
.lp-su { font-size: 11px; color: var(--text-3); font-weight: 500; margin-top: 2px;}
|
|
|
|
.lp-bd { padding: 14px 16px; font-size: 12px; color: var(--text-2); line-height: 1.6; }
|
|
|
|
.ff { margin-bottom: 12px; }
|
|
.ff label {
|
|
display: block; font-size: 10px; font-weight: 700;
|
|
color: var(--text-3); text-transform: uppercase;
|
|
letter-spacing: 0.05em; margin-bottom: 6px;
|
|
}
|
|
.ff input, .ff select {
|
|
width: 100%; padding: 9px 12px; font-size: 12px; font-family: inherit;
|
|
border: 1.5px solid var(--border); border-radius: var(--r3);
|
|
color: var(--navy); transition: all 0.2s; background: var(--border-l);
|
|
}
|
|
.ff input:focus, .ff select:focus {
|
|
outline: none; border-color: var(--green);
|
|
box-shadow: 0 0 0 3px rgba(16,185,129,0.15); background: #fff;
|
|
}
|
|
|
|
.lp-btns {
|
|
padding: 12px 16px 16px;
|
|
background: var(--border-l);
|
|
border-top: 1px solid var(--border-l);
|
|
display: flex; gap: 8px;
|
|
}
|
|
.btn-sv {
|
|
flex: 1; padding: 10px; border-radius: var(--r3); border: none;
|
|
font-family: inherit; font-size: 12px; font-weight: 700;
|
|
color: white; cursor: pointer; transition: all 0.2s;
|
|
display: flex; align-items: center; justify-content: center; gap: 6px;
|
|
}
|
|
.btn-sv.g { background: var(--green); }
|
|
.btn-sv.g:hover { background: var(--green-d); }
|
|
.btn-sv.b { background: var(--blue); }
|
|
.btn-sv.b:hover { background: var(--blue-d); }
|
|
.btn-sv.r { background: var(--red); }
|
|
.btn-sv.r:hover { background: #DC2626; }
|
|
.btn-sv.w { background: var(--amber); color: #fff;}
|
|
.btn-sv.w:hover { background: #D97706; }
|
|
.btn-sv:active { transform: scale(0.98); }
|
|
|
|
.btn-cncl {
|
|
padding: 10px 14px; border-radius: var(--r3); border: 1.5px solid var(--border);
|
|
background: white; font-family: inherit; font-size: 12px; font-weight: 700;
|
|
color: var(--text-2); cursor: pointer; transition: all 0.2s;
|
|
}
|
|
.btn-cncl:hover { background: var(--border-l); color: var(--navy); }
|
|
|
|
/* ── MODERN MARKERS ── */
|
|
.modern-marker {
|
|
position: relative;
|
|
width: 30px;
|
|
height: 42px;
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: center;
|
|
}
|
|
.modern-marker .m-pin {
|
|
position: relative;
|
|
width: 28px;
|
|
height: 28px;
|
|
background: var(--m-color);
|
|
border-radius: 50% 50% 50% 0;
|
|
transform: rotate(-45deg);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 4px 12px rgba(15,23,42,0.3);
|
|
border: 2px solid white;
|
|
z-index: 2;
|
|
transition: transform 0.2s;
|
|
}
|
|
.modern-marker:hover .m-pin {
|
|
transform: rotate(-45deg) scale(1.1);
|
|
}
|
|
.modern-marker .m-pin svg {
|
|
transform: rotate(45deg);
|
|
display: block;
|
|
}
|
|
.modern-marker .m-pulse {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 12px;
|
|
height: 12px;
|
|
background: var(--m-color);
|
|
border-radius: 50%;
|
|
opacity: 0.6;
|
|
animation: mPulse 2s infinite ease-out;
|
|
z-index: 1;
|
|
}
|
|
@keyframes mPulse {
|
|
0% { transform: translateX(-50%) scale(1); opacity: 0.6; }
|
|
100% { transform: translateX(-50%) scale(3); opacity: 0; }
|
|
}
|
|
|
|
/* ── LEAFLET LAYERS CONTROL ── */
|
|
.leaflet-control-layers {
|
|
border: none !important;
|
|
border-radius: 16px !important;
|
|
box-shadow: 0 12px 32px rgba(15,23,42,0.12), 0 0 0 1px rgba(226,232,240,0.8) !important;
|
|
background: rgba(255,255,255,0.95) !important;
|
|
backdrop-filter: blur(16px);
|
|
padding: 6px !important;
|
|
font-family: 'Plus Jakarta Sans', sans-serif !important;
|
|
margin-top: 16px !important;
|
|
margin-right: 16px !important;
|
|
}
|
|
.leaflet-control-layers-list {
|
|
margin-bottom: 0 !important;
|
|
}
|
|
.leaflet-control-layers-overlays label {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-direction: row;
|
|
padding: 8px 12px;
|
|
margin: 2px 0;
|
|
border-radius: 10px;
|
|
cursor: pointer;
|
|
transition: background 0.2s;
|
|
}
|
|
.leaflet-control-layers-overlays label:hover {
|
|
background: var(--blue-l);
|
|
}
|
|
.leaflet-control-layers-overlays label > span {
|
|
display: flex;
|
|
align-items: center;
|
|
flex: 1;
|
|
}
|
|
.leaflet-control-layers-selector {
|
|
margin: 0 12px 0 0 !important;
|
|
width: 16px;
|
|
height: 16px;
|
|
cursor: pointer;
|
|
accent-color: var(--blue);
|
|
flex-shrink: 0;
|
|
}
|
|
.layer-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--navy);
|
|
margin-left: 2px;
|
|
}
|
|
|
|
/* ── SEMBUNYIKAN TOOLBAR GEOMAN DEFAULT ── */
|
|
.leaflet-pm-toolbar { display: none !important; }
|
|
|
|
/* ── DRAW GUIDE BADGE ── */
|
|
#draw-guide {
|
|
background: rgba(255,255,255,0.95);
|
|
backdrop-filter: blur(16px);
|
|
border-radius: 14px;
|
|
box-shadow: 0 8px 24px rgba(15,23,42,0.12), 0 0 0 1px rgba(226,232,240,0.8);
|
|
padding: 12px 14px;
|
|
width: 100%;
|
|
margin-top: 4px;
|
|
display: none;
|
|
}
|
|
#draw-guide.visible { display: block; animation: fadeInUp 0.3s ease; }
|
|
@keyframes fadeInUp {
|
|
from { opacity: 0; transform: translateY(10px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
.dg-title {
|
|
font-size: 11px; font-weight: 800; color: var(--navy);
|
|
text-transform: uppercase; letter-spacing: 0.05em;
|
|
margin-bottom: 8px;
|
|
display: flex; align-items: center; gap: 6px;
|
|
}
|
|
.dg-step {
|
|
display: flex; align-items: flex-start; gap: 8px;
|
|
font-size: 11px; color: var(--text-2); font-weight: 500;
|
|
margin-bottom: 6px; line-height: 1.4;
|
|
}
|
|
.dg-step:last-child { margin-bottom: 0; }
|
|
.dg-num {
|
|
width: 16px; height: 16px; border-radius: 50%;
|
|
background: var(--blue); color: white;
|
|
font-size: 9px; font-weight: 800;
|
|
display: flex; align-items: center; justify-content: center;
|
|
flex-shrink: 0; margin-top: 1px;
|
|
}
|
|
|
|
/* ── TOOL PANEL KUSTOM ── */
|
|
#tool-panel {
|
|
position: fixed;
|
|
top: 64px;
|
|
left: 16px;
|
|
z-index: 1100;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
}
|
|
.tp-section-label {
|
|
font-size: 11px;
|
|
font-weight: 800;
|
|
color: var(--navy);
|
|
background: rgba(226,232,240,0.8);
|
|
backdrop-filter: blur(8px);
|
|
padding: 4px 10px;
|
|
border-radius: 6px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.1em;
|
|
margin-top: 8px;
|
|
align-self: flex-start;
|
|
}
|
|
.tp-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 9px 12px;
|
|
border-radius: 12px;
|
|
background: rgba(255,255,255,0.95);
|
|
backdrop-filter: blur(16px);
|
|
box-shadow: 0 4px 16px rgba(15,23,42,0.1), 0 0 0 1px rgba(226,232,240,0.8);
|
|
border: none;
|
|
cursor: pointer;
|
|
text-align: left;
|
|
transition: all 0.2s;
|
|
width: 100%;
|
|
min-width: 180px;
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
}
|
|
.tp-btn:hover {
|
|
box-shadow: 0 8px 24px rgba(15,23,42,0.15), 0 0 0 1px rgba(226,232,240,0.8);
|
|
transform: translateX(3px);
|
|
}
|
|
.tp-btn.active {
|
|
background: var(--navy);
|
|
box-shadow: 0 8px 24px rgba(15,23,42,0.3);
|
|
}
|
|
.tp-btn.active .tp-name { color: white; }
|
|
.tp-btn.active .tp-desc { color: rgba(255,255,255,0.6); }
|
|
.tp-icon {
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 9px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
}
|
|
.tp-text { flex: 1; }
|
|
.tp-name {
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
color: var(--navy);
|
|
line-height: 1.2;
|
|
}
|
|
.tp-desc {
|
|
font-size: 10px;
|
|
color: var(--text-3);
|
|
font-weight: 500;
|
|
margin-top: 2px;
|
|
}
|
|
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- NAVBAR -->
|
|
<nav id="navbar">
|
|
<a href="#" class="nav-brand">
|
|
<div class="nav-brand-logo">
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z"></path>
|
|
<circle cx="12" cy="10" r="3"></circle>
|
|
</svg>
|
|
</div>
|
|
<div class="nav-brand-text">WebGIS SPBU & Parsil Tanah</div>
|
|
</a>
|
|
</nav>
|
|
|
|
<div id="map"></div>
|
|
|
|
<!-- TOOL PANEL KUSTOM -->
|
|
<div id="tool-panel">
|
|
<div class="tp-section-label">Tambah Data</div>
|
|
|
|
<button class="tp-btn" id="tool-spbu" onclick="toggleTool('spbu')" title="Klik titik di peta untuk menambahkan SPBU">
|
|
<div class="tp-icon" style="background:#EFF6FF;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#2563EB" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z"/><circle cx="12" cy="10" r="3"/>
|
|
</svg>
|
|
</div>
|
|
<div class="tp-text">
|
|
<div class="tp-name">Tambah SPBU</div>
|
|
<div class="tp-desc">Klik titik di peta</div>
|
|
</div>
|
|
</button>
|
|
|
|
<button class="tp-btn" id="tool-jalan" onclick="toggleTool('jalan')">
|
|
<div class="tp-icon" style="background:#ECFDF5;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#10B981" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M3 6h18M3 12h18M3 18h18"/>
|
|
</svg>
|
|
</div>
|
|
<div class="tp-text">
|
|
<div class="tp-name">Gambar Jalan</div>
|
|
<div class="tp-desc">Klik titik-titik jalur jalan</div>
|
|
</div>
|
|
</button>
|
|
|
|
<button class="tp-btn" id="tool-polygon" onclick="toggleTool('polygon')">
|
|
<div class="tp-icon" style="background:#FFFBEB;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#F59E0B" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
<polygon points="3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21"/>
|
|
</svg>
|
|
</div>
|
|
<div class="tp-text">
|
|
<div class="tp-name">Gambar Parsil</div>
|
|
<div class="tp-desc">Klik sudut-sudut bidang tanah</div>
|
|
</div>
|
|
</button>
|
|
|
|
<button class="tp-btn" id="tool-rectangle" onclick="toggleTool('rectangle')">
|
|
<div class="tp-icon" style="background:#FFF7ED;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#EA580C" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
<rect x="3" y="5" width="18" height="14" rx="2"/>
|
|
</svg>
|
|
</div>
|
|
<div class="tp-text">
|
|
<div class="tp-name">Parsil Kotak</div>
|
|
<div class="tp-desc">Seret untuk buat persegi panjang</div>
|
|
</div>
|
|
</button>
|
|
|
|
<div class="tp-section-label">Edit</div>
|
|
|
|
<button class="tp-btn" id="tool-edit" onclick="toggleTool('edit')">
|
|
<div class="tp-icon" style="background:#F0FDF4;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#16A34A" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
|
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
</svg>
|
|
</div>
|
|
<div class="tp-text">
|
|
<div class="tp-name">Edit Bentuk</div>
|
|
<div class="tp-desc">Geser titik sudut layer</div>
|
|
</div>
|
|
</button>
|
|
|
|
<button class="tp-btn" id="tool-drag" onclick="toggleTool('drag')">
|
|
<div class="tp-icon" style="background:#F5F3FF;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#7C3AED" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="5 9 2 12 5 15"/><polyline points="9 5 12 2 15 5"/>
|
|
<polyline points="15 19 12 22 9 19"/><polyline points="19 9 22 12 19 15"/>
|
|
<line x1="2" y1="12" x2="22" y2="12"/><line x1="12" y1="2" x2="12" y2="22"/>
|
|
</svg>
|
|
</div>
|
|
<div class="tp-text">
|
|
<div class="tp-name">Pindah Layer</div>
|
|
<div class="tp-desc">Seret layer ke posisi baru</div>
|
|
</div>
|
|
</button>
|
|
|
|
<button class="tp-btn" id="tool-delete" onclick="toggleTool('delete')">
|
|
<div class="tp-icon" style="background:#FEF2F2;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#EF4444" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="3 6 5 6 21 6"/>
|
|
<path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>
|
|
<path d="M10 11v6"/><path d="M14 11v6"/>
|
|
<path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/>
|
|
</svg>
|
|
</div>
|
|
<div class="tp-text">
|
|
<div class="tp-name">Hapus Layer</div>
|
|
<div class="tp-desc">Klik layer untuk menghapus</div>
|
|
</div>
|
|
</button>
|
|
|
|
<!-- KOTAK PANDUAN MENGGAMBAR -->
|
|
<div id="draw-guide"></div>
|
|
</div>
|
|
|
|
<script>
|
|
// ============================================================
|
|
// INISIALISASI PETA
|
|
// ============================================================
|
|
const map = L.map('map', { zoomControl: false }).setView([-0.0548, 109.3494], 13);
|
|
L.control.zoom({ position: 'bottomright' }).addTo(map);
|
|
|
|
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
maxZoom: 19,
|
|
attribution: '© OpenStreetMap'
|
|
}).addTo(map);
|
|
|
|
let popup = L.popup();
|
|
let tempLatLng = null;
|
|
|
|
// ============================================================
|
|
// DEKLARASI LAYER
|
|
// ============================================================
|
|
let layerSPBU_24Jam = L.layerGroup().addTo(map);
|
|
let layerSPBU_Reguler = L.layerGroup().addTo(map);
|
|
let jalanLayer = L.layerGroup().addTo(map);
|
|
let kavlingLayer = L.layerGroup().addTo(map);
|
|
|
|
let pilihanLayer = {
|
|
"<span class='layer-item'><svg width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='#2563EB' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><path d='M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z'></path><circle cx='12' cy='10' r='3'></circle></svg> <span>SPBU Buka 24 Jam</span></span>": layerSPBU_24Jam,
|
|
"<span class='layer-item'><svg width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='#EF4444' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><path d='M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z'></path><circle cx='12' cy='10' r='3'></circle></svg> <span>SPBU Tutup Malam</span></span>": layerSPBU_Reguler,
|
|
"<span class='layer-item'><svg width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='#10B981' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><path d='M3 6h18M3 12h18M3 18h18'/></svg> <span>Jaringan Jalan</span></span>": jalanLayer,
|
|
"<span class='layer-item'><svg width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='#F59E0B' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polygon points='3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21'></polygon><line x1='9' y1='3' x2='9' y2='18'></line><line x1='15' y1='6' x2='15' y2='21'></line></svg> <span>Parsil Tanah</span></span>": kavlingLayer
|
|
};
|
|
L.control.layers(null, pilihanLayer, { collapsed: false, position: 'topright' }).addTo(map);
|
|
|
|
// ============================================================
|
|
// DEFINISI ICON WARNA
|
|
// ============================================================
|
|
const customMarkerSVG = (color) => `
|
|
<div class="modern-marker" style="--m-color: ${color}">
|
|
<div class="m-pulse"></div>
|
|
<div class="m-pin">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z"></path>
|
|
<circle cx="12" cy="10" r="3"></circle>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
const iconMerah = L.divIcon({
|
|
className: 'custom-div-icon',
|
|
html: customMarkerSVG('#EF4444'),
|
|
iconSize: [30, 42],
|
|
iconAnchor: [15, 42],
|
|
popupAnchor: [0, -32]
|
|
});
|
|
|
|
const iconBiru = L.divIcon({
|
|
className: 'custom-div-icon',
|
|
html: customMarkerSVG('#2563EB'),
|
|
iconSize: [30, 42],
|
|
iconAnchor: [15, 42],
|
|
popupAnchor: [0, -32]
|
|
});
|
|
|
|
// ============================================================
|
|
// KLIK PETA - FORM TAMBAH SPBU SAJA
|
|
// ============================================================
|
|
map.on('click', function(e) {
|
|
if (map.pm.globalDrawModeEnabled()) return;
|
|
tempLatLng = e.latlng;
|
|
let formHTML = `
|
|
<div class="lp-hd">
|
|
<div class="lp-ti">Tambah SPBU Baru</div>
|
|
<div class="lp-su">Input data lokasi stasiun pengisian bahan bakar</div>
|
|
</div>
|
|
<div class="lp-bd">
|
|
<div class="ff">
|
|
<label>Nama SPBU</label>
|
|
<input type="text" id="nama_spbu" placeholder="Masukkan nama SPBU" required>
|
|
</div>
|
|
<div class="ff">
|
|
<label>Nama Pengelola</label>
|
|
<input type="text" id="nama_pengelola" placeholder="Masukkan nama pengelola" required>
|
|
</div>
|
|
<div class="ff">
|
|
<label>No. Whatsapp</label>
|
|
<input type="text" id="no_wa" placeholder="Contoh: 0812..." required>
|
|
</div>
|
|
<div class="ff" style="margin-bottom:0;">
|
|
<label>Buka 24 Jam?</label>
|
|
<select id="buka_24jam">
|
|
<option value="" disabled selected>Pilih status</option>
|
|
<option value="Ya">Ya (Buka 24 Jam)</option>
|
|
<option value="Tidak">Tidak</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="lp-btns">
|
|
<button class="btn-sv g" onclick="simpanSPBU()">Simpan Data</button>
|
|
</div>
|
|
`;
|
|
popup.setLatLng(e.latlng).setContent(formHTML).openOn(map);
|
|
});
|
|
|
|
// ============================================================
|
|
// BAGIAN SPBU CRUD
|
|
// ============================================================
|
|
function simpanSPBU() {
|
|
let nama = document.getElementById('nama_spbu').value;
|
|
let pengelola = document.getElementById('nama_pengelola').value;
|
|
let wa = document.getElementById('no_wa').value;
|
|
let jam = document.getElementById('buka_24jam').value;
|
|
if (!nama || !pengelola || !wa || jam === "") { alert("Harap isi semua data termasuk status 24 jam!"); return; }
|
|
|
|
let fd = new FormData();
|
|
fd.append('nama_spbu', nama); fd.append('nama_pengelola', pengelola); fd.append('no_wa', wa);
|
|
fd.append('buka_24jam', jam); fd.append('lat', tempLatLng.lat); fd.append('lng', tempLatLng.lng);
|
|
|
|
let btn = document.querySelector('.btn-sv.g');
|
|
let oldText = btn.innerHTML;
|
|
btn.innerHTML = "Menyimpan...";
|
|
btn.disabled = true;
|
|
|
|
fetch('simpan.php', { method: 'POST', body: fd })
|
|
.then(res => res.text()).then(data => {
|
|
if (data.includes("Sukses")) { map.closePopup(); muatDataSPBU(); }
|
|
else { alert("Gagal menyimpan: " + data); btn.innerHTML = oldText; btn.disabled = false;}
|
|
}).catch(error => { console.error('Error:', error); btn.innerHTML = oldText; btn.disabled = false; });
|
|
}
|
|
|
|
function muatDataSPBU() {
|
|
fetch('tampil.php')
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
layerSPBU_24Jam.clearLayers();
|
|
layerSPBU_Reguler.clearLayers();
|
|
data.forEach(spbu => {
|
|
let is24 = (spbu.buka_24jam.trim() === 'Ya');
|
|
|
|
let markerHTML = `
|
|
<div id="info_${spbu.id}">
|
|
<div class="lp-hd">
|
|
<div class="lp-ti">${spbu.nama_spbu}</div>
|
|
<div class="lp-su">Pengelola: ${spbu.nama_pengelola || '-'}</div>
|
|
<div class="lp-su">Kontak: ${spbu.no_wa}</div>
|
|
</div>
|
|
<div class="lp-bd">
|
|
Status: <b>${is24 ? '<span style="color:#10B981;">Buka 24 Jam</span>' : '<span style="color:#EF4444;">Tutup Malam</span>'}</b><br>
|
|
<i style="font-size:10px; color:#94A3B8; display:block; margin-top:8px;">Geser pin untuk mengubah lokasi koordinat.</i>
|
|
</div>
|
|
<div class="lp-btns" style="display:flex; gap:6px;">
|
|
<button class="btn-sv w" onclick="bukaFormEdit(${spbu.id})">Edit</button>
|
|
<button class="btn-sv r" onclick="hapusSPBU(${spbu.id})">Hapus</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="form_${spbu.id}" style="display:none;">
|
|
<div class="lp-hd">
|
|
<div class="lp-ti">Edit SPBU</div>
|
|
</div>
|
|
<div class="lp-bd">
|
|
<div class="ff">
|
|
<label>Nama SPBU</label>
|
|
<input type="text" id="edit_nama_${spbu.id}" value="${spbu.nama_spbu}">
|
|
</div>
|
|
<div class="ff">
|
|
<label>Nama Pengelola</label>
|
|
<input type="text" id="edit_pengelola_${spbu.id}" value="${spbu.nama_pengelola || ''}">
|
|
</div>
|
|
<div class="ff">
|
|
<label>No. Whatsapp</label>
|
|
<input type="text" id="edit_wa_${spbu.id}" value="${spbu.no_wa}">
|
|
</div>
|
|
<div class="ff" style="margin-bottom:0;">
|
|
<label>Buka 24 Jam?</label>
|
|
<select id="edit_jam_${spbu.id}">
|
|
<option value="Ya" ${is24 ? 'selected' : ''}>Ya</option>
|
|
<option value="Tidak" ${!is24 ? 'selected' : ''}>Tidak</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="lp-btns">
|
|
<button class="btn-cncl" onclick="batalEdit(${spbu.id})">Batal</button>
|
|
<button class="btn-sv b" onclick="simpanEdit(${spbu.id})">Update</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
let opsiMarker = { draggable: true, icon: is24 ? iconBiru : iconMerah };
|
|
let wadahTujuan = is24 ? layerSPBU_24Jam : layerSPBU_Reguler;
|
|
|
|
let marker = L.marker([spbu.lat, spbu.lng], opsiMarker).addTo(wadahTujuan).bindPopup(markerHTML);
|
|
|
|
marker.on('dragend', function(e) {
|
|
let posisiBaru = e.target.getLatLng();
|
|
if (confirm("Simpan posisi koordinat baru untuk SPBU ini?")) {
|
|
updateLokasiSPBU(spbu.id, posisiBaru.lat, posisiBaru.lng);
|
|
} else { muatDataSPBU(); }
|
|
});
|
|
});
|
|
}).catch(error => console.error('Error:', error));
|
|
}
|
|
|
|
function bukaFormEdit(id) {
|
|
document.getElementById(`info_${id}`).style.display = 'none';
|
|
document.getElementById(`form_${id}`).style.display = 'block';
|
|
}
|
|
function batalEdit(id) {
|
|
document.getElementById(`info_${id}`).style.display = 'block';
|
|
document.getElementById(`form_${id}`).style.display = 'none';
|
|
}
|
|
function simpanEdit(id) {
|
|
let fd = new FormData();
|
|
fd.append('id', id);
|
|
fd.append('nama_spbu', document.getElementById(`edit_nama_${id}`).value);
|
|
fd.append('nama_pengelola', document.getElementById(`edit_pengelola_${id}`).value);
|
|
fd.append('no_wa', document.getElementById(`edit_wa_${id}`).value);
|
|
fd.append('buka_24jam', document.getElementById(`edit_jam_${id}`).value);
|
|
|
|
fetch('update.php', { method: 'POST', body: fd })
|
|
.then(res => res.text()).then(data => {
|
|
if (data.includes("Sukses")) { map.closePopup(); muatDataSPBU(); }
|
|
else { alert("Gagal update: " + data); }
|
|
}).catch(error => console.error('Error:', error));
|
|
}
|
|
function hapusSPBU(id) {
|
|
if (!confirm("Apakah Anda yakin ingin menghapus SPBU ini?")) return;
|
|
let fd = new FormData(); fd.append('id', id);
|
|
fetch('hapus.php', { method: 'POST', body: fd })
|
|
.then(res => res.text()).then(data => {
|
|
if (data.includes("Sukses")) { map.closePopup(); muatDataSPBU(); }
|
|
else { alert("Gagal menghapus: " + data); }
|
|
}).catch(error => console.error('Error:', error));
|
|
}
|
|
function updateLokasiSPBU(id, lat, lng) {
|
|
let fd = new FormData(); fd.append('id', id); fd.append('lat', lat); fd.append('lng', lng);
|
|
fetch('update_lokasi.php', { method: 'POST', body: fd })
|
|
.then(res => res.text()).then(data => {
|
|
if (!data.includes("Sukses")) { alert("Gagal memperbarui posisi: " + data); muatDataSPBU(); }
|
|
}).catch(error => console.error('Error:', error));
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// BAGIAN JALAN
|
|
// ============================================================
|
|
function muatDataJalan() {
|
|
fetch('tampil_jalan.php').then(res => res.json()).then(data => {
|
|
jalanLayer.clearLayers();
|
|
data.forEach(jalan => {
|
|
let warnaJalan = '#1E293B'; // navy default
|
|
if (jalan.status_jalan === 'Nasional') warnaJalan = '#EF4444';
|
|
else if (jalan.status_jalan === 'Provinsi') warnaJalan = '#2563EB';
|
|
else if (jalan.status_jalan === 'Kabupaten') warnaJalan = '#10B981';
|
|
|
|
let geojsonData = JSON.parse(jalan.geojson);
|
|
L.geoJSON(geojsonData, {
|
|
style: { color: warnaJalan, weight: 5, opacity: 0.8 },
|
|
onEachFeature: function (feature, layer) {
|
|
let infoPilihan = `
|
|
<div id="info_jalan_${jalan.id}">
|
|
<div class="lp-hd">
|
|
<div class="lp-ti">${jalan.nama_jalan}</div>
|
|
</div>
|
|
<div class="lp-bd">
|
|
Status: <b>${jalan.status_jalan}</b><br>
|
|
Panjang: ${jalan.panjang_jalan} m
|
|
</div>
|
|
<div class="lp-btns" style="display: flex; gap: 6px;">
|
|
<button class="btn-sv w" onclick="bukaFormEditJalan(${jalan.id})">Edit</button>
|
|
<button class="btn-sv r" onclick="hapusJalan(${jalan.id})">Hapus</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="form_jalan_${jalan.id}" style="display:none;">
|
|
<div class="lp-hd">
|
|
<div class="lp-ti">Edit Jaringan Jalan</div>
|
|
</div>
|
|
<div class="lp-bd">
|
|
<div class="ff">
|
|
<label>Nama Jalan</label>
|
|
<input type="text" id="edit_nama_jalan_${jalan.id}" value="${jalan.nama_jalan}">
|
|
</div>
|
|
<div class="ff" style="margin-bottom:0;">
|
|
<label>Status Jalan</label>
|
|
<select id="edit_status_jalan_${jalan.id}">
|
|
<option value="Nasional" ${jalan.status_jalan === 'Nasional' ? 'selected' : ''}>Jalan Nasional</option>
|
|
<option value="Provinsi" ${jalan.status_jalan === 'Provinsi' ? 'selected' : ''}>Jalan Provinsi</option>
|
|
<option value="Kabupaten" ${jalan.status_jalan === 'Kabupaten' ? 'selected' : ''}>Jalan Kabupaten</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="lp-btns">
|
|
<button class="btn-cncl" onclick="batalEditJalan(${jalan.id})">Batal</button>
|
|
<button class="btn-sv b" onclick="simpanEditJalan(${jalan.id})">Update</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
layer.bindPopup(infoPilihan);
|
|
}
|
|
}).addTo(jalanLayer);
|
|
});
|
|
}).catch(error => console.error('Error:', error));
|
|
}
|
|
|
|
function hapusJalan(id) {
|
|
if (!confirm("Hapus data jaringan jalan ini dari peta?")) return;
|
|
let fd = new FormData(); fd.append('id', id);
|
|
fetch('hapus_jalan.php', { method: 'POST', body: fd })
|
|
.then(res => res.text()).then(data => {
|
|
if (data.includes("Sukses")) { map.closePopup(); muatDataJalan(); }
|
|
else { alert("Gagal menghapus: " + data); }
|
|
}).catch(error => console.error('Error:', error));
|
|
}
|
|
function bukaFormEditJalan(id) {
|
|
document.getElementById(`info_jalan_${id}`).style.display = 'none';
|
|
document.getElementById(`form_jalan_${id}`).style.display = 'block';
|
|
}
|
|
function batalEditJalan(id) {
|
|
document.getElementById(`info_jalan_${id}`).style.display = 'block';
|
|
document.getElementById(`form_jalan_${id}`).style.display = 'none';
|
|
}
|
|
function simpanEditJalan(id) {
|
|
let namaBaru = document.getElementById(`edit_nama_jalan_${id}`).value;
|
|
let statusBaru = document.getElementById(`edit_status_jalan_${id}`).value;
|
|
if (!namaBaru) return alert("Nama jalan tidak boleh kosong!");
|
|
let fd = new FormData(); fd.append('id', id); fd.append('nama_jalan', namaBaru); fd.append('status_jalan', statusBaru);
|
|
fetch('update_jalan.php', { method: 'POST', body: fd })
|
|
.then(res => res.text()).then(data => {
|
|
if (data.includes("Sukses")) { map.closePopup(); muatDataJalan(); }
|
|
else { alert("Gagal update: " + data); }
|
|
}).catch(error => console.error('Error:', error));
|
|
}
|
|
|
|
// ============================================================
|
|
// BAGIAN KAVLING
|
|
// ============================================================
|
|
function muatDataKavling() {
|
|
fetch('tampil_kavling.php').then(res => res.json()).then(data => {
|
|
kavlingLayer.clearLayers();
|
|
data.forEach(kavling => {
|
|
let warnaKavling = '#94A3B8';
|
|
if (kavling.status_kepemilikan === 'SHM') warnaKavling = '#10B981';
|
|
else if (kavling.status_kepemilikan === 'HGB') warnaKavling = '#F59E0B';
|
|
else if (kavling.status_kepemilikan === 'HGU') warnaKavling = '#EF4444';
|
|
else if (kavling.status_kepemilikan === 'HP') warnaKavling = '#2563EB';
|
|
|
|
let badgeColor = warnaKavling;
|
|
let geojsonData = JSON.parse(kavling.geojson);
|
|
|
|
L.geoJSON(geojsonData, {
|
|
style: { color: warnaKavling, fillColor: warnaKavling, weight: 2, fillOpacity: 0.35 },
|
|
onEachFeature: function (feature, layer) {
|
|
let popupHTML = `
|
|
<div id="info_kav_${kavling.id}">
|
|
<div class="lp-hd">
|
|
<div class="lp-ti">Parsil Tanah</div>
|
|
<div class="lp-su" style="margin-top:4px;">
|
|
<span style="display:inline-block; background:${badgeColor}22; color:${badgeColor}; border:1px solid ${badgeColor}44; border-radius:20px; padding:2px 10px; font-size:11px; font-weight:700;">${kavling.status_kepemilikan}</span>
|
|
</div>
|
|
</div>
|
|
<div class="lp-bd">
|
|
Luas Tanah: <b>${parseFloat(kavling.luas_tanah).toLocaleString('id-ID')} m²</b>
|
|
</div>
|
|
<div class="lp-btns" style="display:flex; gap:6px;">
|
|
<button class="btn-sv w" onclick="bukaFormEditKavling(${kavling.id})">Edit</button>
|
|
<button class="btn-sv r" onclick="hapusKavling(${kavling.id})">Hapus</button>
|
|
</div>
|
|
</div>
|
|
<div id="form_kav_${kavling.id}" style="display:none;">
|
|
<div class="lp-hd">
|
|
<div class="lp-ti">Edit Parsil Tanah</div>
|
|
</div>
|
|
<div class="lp-bd">
|
|
<div class="ff" style="margin-bottom:0;">
|
|
<label>Status Kepemilikan</label>
|
|
<select id="edit_status_kav_${kavling.id}">
|
|
<option value="SHM" ${kavling.status_kepemilikan === 'SHM' ? 'selected' : ''}>Sertifikat Hak Milik (SHM)</option>
|
|
<option value="HGB" ${kavling.status_kepemilikan === 'HGB' ? 'selected' : ''}>Hak Guna Bangunan (HGB)</option>
|
|
<option value="HGU" ${kavling.status_kepemilikan === 'HGU' ? 'selected' : ''}>Hak Guna Usaha (HGU)</option>
|
|
<option value="HP" ${kavling.status_kepemilikan === 'HP' ? 'selected' : ''}>Hak Pakai (HP)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="lp-btns">
|
|
<button class="btn-cncl" onclick="batalEditKavling(${kavling.id})">Batal</button>
|
|
<button class="btn-sv b" onclick="simpanEditKavling(${kavling.id})">Update</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
layer.bindPopup(popupHTML);
|
|
}
|
|
}).addTo(kavlingLayer);
|
|
});
|
|
}).catch(error => console.error('Error:', error));
|
|
}
|
|
|
|
function bukaFormEditKavling(id) {
|
|
document.getElementById(`info_kav_${id}`).style.display = 'none';
|
|
document.getElementById(`form_kav_${id}`).style.display = 'block';
|
|
}
|
|
function batalEditKavling(id) {
|
|
document.getElementById(`info_kav_${id}`).style.display = 'block';
|
|
document.getElementById(`form_kav_${id}`).style.display = 'none';
|
|
}
|
|
function hapusKavling(id) {
|
|
if (!confirm('Hapus data parsil tanah ini dari peta?')) return;
|
|
let fd = new FormData(); fd.append('id', id);
|
|
fetch('hapus_kavling.php', { method: 'POST', body: fd })
|
|
.then(res => res.text()).then(data => {
|
|
if (data.includes('Sukses')) { map.closePopup(); muatDataKavling(); }
|
|
else { alert('Gagal menghapus: ' + data); }
|
|
}).catch(error => console.error('Error:', error));
|
|
}
|
|
function simpanEditKavling(id) {
|
|
let status = document.getElementById(`edit_status_kav_${id}`).value;
|
|
let fd = new FormData(); fd.append('id', id); fd.append('status_kepemilikan', status);
|
|
fetch('update_kavling.php', { method: 'POST', body: fd })
|
|
.then(res => res.text()).then(data => {
|
|
if (data.includes('Sukses')) { map.closePopup(); muatDataKavling(); }
|
|
else { alert('Gagal update: ' + data); }
|
|
}).catch(error => console.error('Error:', error));
|
|
}
|
|
|
|
// ============================================================
|
|
// GEOMAN - TOOLBAR & GAMBAR
|
|
// ============================================================
|
|
map.pm.addControls({
|
|
position: 'topleft',
|
|
drawMarker: false,
|
|
drawPolyline: true,
|
|
drawRectangle: true,
|
|
drawPolygon: true,
|
|
drawCircle: false,
|
|
cutLayer: false,
|
|
editMode: true,
|
|
dragMode: true,
|
|
removalMode: true,
|
|
});
|
|
|
|
// Aktifkan bahasa Indonesia untuk tooltip interaktif (petunjuk penggunaan tombol)
|
|
map.pm.setLang('id');
|
|
|
|
// ── FUNGSI TOGGLE TOOL PANEL ──
|
|
let activeTool = null;
|
|
function setActiveBtn(id) {
|
|
document.querySelectorAll('.tp-btn').forEach(b => b.classList.remove('active'));
|
|
if (id) document.getElementById('tool-' + id)?.classList.add('active');
|
|
}
|
|
function deactivateAll() {
|
|
map.pm.disableDraw();
|
|
map.pm.disableGlobalEditMode();
|
|
map.pm.disableGlobalDragMode();
|
|
map.pm.disableGlobalRemovalMode();
|
|
activeTool = null;
|
|
setActiveBtn(null);
|
|
guideEl.classList.remove('visible');
|
|
}
|
|
function toggleTool(tool) {
|
|
if (activeTool === tool) { deactivateAll(); return; }
|
|
deactivateAll();
|
|
activeTool = tool;
|
|
setActiveBtn(tool);
|
|
if (tool === 'jalan') { map.pm.enableDraw('Line'); showGuide('line'); }
|
|
if (tool === 'polygon') { map.pm.enableDraw('Polygon'); showGuide('polygon'); }
|
|
if (tool === 'rectangle') { map.pm.enableDraw('Rectangle'); showGuide('polygon'); }
|
|
if (tool === 'edit') { map.pm.enableGlobalEditMode(); }
|
|
if (tool === 'drag') { map.pm.enableGlobalDragMode(); }
|
|
if (tool === 'delete') { map.pm.enableGlobalRemovalMode(); }
|
|
if (tool === 'spbu') {
|
|
// Mode SPBU cukup dengan klik di peta (sudah default)
|
|
}
|
|
}
|
|
// Sinkronkan tombol panel saat Geoman selesai menggambar
|
|
map.on('pm:drawend', deactivateAll);
|
|
map.on('pm:create', () => { deactivateAll(); });
|
|
|
|
// ── PANDUAN MENGGAMBAR (badge bawah-kiri) ──
|
|
const guidePolygon = `
|
|
<div class="dg-title">
|
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#F59E0B" stroke-width="2.5"><polygon points="3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21"/></svg>
|
|
Cara Tambah Parsil Tanah
|
|
</div>
|
|
<div class="dg-step"><div class="dg-num">1</div><span>Klik <b>Gambar Parsil / Parsil Kotak</b> di panel kiri</span></div>
|
|
<div class="dg-step"><div class="dg-num">2</div><span>Klik di peta untuk membuat titik sudut bidang tanah</span></div>
|
|
<div class="dg-step"><div class="dg-num">3</div><span>Double klik atau klik titik awal untuk menutup area</span></div>
|
|
<div class="dg-step"><div class="dg-num">4</div><span>Isi status kepemilikan lalu klik <b>Simpan</b></span></div>
|
|
`;
|
|
const guideLine = `
|
|
<div class="dg-title">
|
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#10B981" stroke-width="2.5"><path d="M3 6h18M3 12h18M3 18h18"/></svg>
|
|
Cara Tambah Jaringan Jalan
|
|
</div>
|
|
<div class="dg-step"><div class="dg-num">1</div><span>Klik <b>Gambar Jalan</b> di panel kiri</span></div>
|
|
<div class="dg-step"><div class="dg-num">2</div><span>Klik di peta untuk menandai titik-titik jalur jalan</span></div>
|
|
<div class="dg-step"><div class="dg-num">3</div><span>Double klik di titik terakhir untuk mengakhiri garis</span></div>
|
|
<div class="dg-step"><div class="dg-num">4</div><span>Isi nama & status jalan lalu klik <b>Simpan</b></span></div>
|
|
`;
|
|
const guideEl = document.getElementById('draw-guide');
|
|
function showGuide(type) {
|
|
if (type === 'polygon') { guideEl.innerHTML = guidePolygon; guideEl.classList.add('visible'); }
|
|
else if (type === 'line') { guideEl.innerHTML = guideLine; guideEl.classList.add('visible'); }
|
|
else { guideEl.classList.remove('visible'); }
|
|
}
|
|
|
|
let layerSementara = null;
|
|
let geojsonSementara = null;
|
|
|
|
map.on('pm:create', function(e) {
|
|
let layer = e.layer;
|
|
let type = e.shape;
|
|
layerSementara = layer;
|
|
geojsonSementara = layer.toGeoJSON();
|
|
|
|
if (type === 'Polygon' || type === 'Rectangle') {
|
|
let luasTanah = turf.area(geojsonSementara);
|
|
let formKavling = `
|
|
<div class="lp-hd">
|
|
<div class="lp-ti">Data Parsil Tanah</div>
|
|
</div>
|
|
<div class="lp-bd">
|
|
<div style="margin-bottom: 12px;">Luas: <b>${luasTanah.toFixed(2)} m²</b></div>
|
|
<div class="ff" style="margin-bottom:0;">
|
|
<label>Status Kepemilikan</label>
|
|
<select id="status_kavling">
|
|
<option value="" disabled selected>Pilih Status...</option>
|
|
<option value="SHM">Sertifikat Hak Milik (SHM)</option>
|
|
<option value="HGB">Sertifikat Hak Guna Bangunan (HGB)</option>
|
|
<option value="HGU">Sertifikat Hak Guna Usaha (HGU)</option>
|
|
<option value="HP">Sertifikat Hak Pakai (HP)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="lp-btns">
|
|
<button class="btn-cncl" onclick="batalGambar()">Batal</button>
|
|
<button class="btn-sv g" onclick="simpanKavling(${luasTanah})">Simpan</button>
|
|
</div>
|
|
`;
|
|
layer.bindPopup(formKavling).openPopup();
|
|
} else if (type === 'Line') {
|
|
let panjangJalan = turf.length(geojsonSementara, {units: 'kilometers'}) * 1000;
|
|
let formJalan = `
|
|
<div class="lp-hd">
|
|
<div class="lp-ti">Jaringan Jalan</div>
|
|
</div>
|
|
<div class="lp-bd">
|
|
<div style="margin-bottom: 12px;">Panjang: <b>${panjangJalan.toFixed(2)} meter</b></div>
|
|
<div class="ff">
|
|
<label>Nama Jalan</label>
|
|
<input type="text" id="nama_jalan" placeholder="Masukkan Nama Jalan">
|
|
</div>
|
|
<div class="ff" style="margin-bottom:0;">
|
|
<label>Status Jalan</label>
|
|
<select id="status_jalan">
|
|
<option value="" disabled selected>Pilih Status...</option>
|
|
<option value="Nasional">Jalan Nasional</option>
|
|
<option value="Provinsi">Jalan Provinsi</option>
|
|
<option value="Kabupaten">Jalan Kabupaten</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="lp-btns">
|
|
<button class="btn-cncl" onclick="batalGambar()">Batal</button>
|
|
<button class="btn-sv g" onclick="simpanJalan(${panjangJalan})">Simpan</button>
|
|
</div>
|
|
`;
|
|
layer.bindPopup(formJalan).openPopup();
|
|
}
|
|
});
|
|
|
|
function simpanKavling(luas) {
|
|
let status = document.getElementById('status_kavling').value;
|
|
if (!status) return alert("Pilih status kepemilikan!");
|
|
let fd = new FormData();
|
|
fd.append('status', status); fd.append('luas', luas.toFixed(2)); fd.append('geojson', JSON.stringify(geojsonSementara));
|
|
|
|
let btn = event.target;
|
|
let oldText = btn.innerHTML;
|
|
btn.innerHTML = "Menyimpan...";
|
|
btn.disabled = true;
|
|
|
|
fetch('simpan_kavling.php', { method: 'POST', body: fd })
|
|
.then(res => res.text()).then(res => {
|
|
if (res.includes("Sukses")) {
|
|
map.closePopup(); map.removeLayer(layerSementara); muatDataKavling();
|
|
} else { alert("Gagal: " + res); btn.innerHTML = oldText; btn.disabled = false;}
|
|
}).catch(error => { console.error('Error AJAX:', error); btn.innerHTML = oldText; btn.disabled = false;});
|
|
}
|
|
|
|
function simpanJalan(panjang) {
|
|
let nama = document.getElementById('nama_jalan').value;
|
|
let status = document.getElementById('status_jalan').value;
|
|
if (!nama || !status) return alert("Isi nama dan status jalan!");
|
|
let fd = new FormData();
|
|
fd.append('nama', nama); fd.append('status', status);
|
|
fd.append('panjang', panjang.toFixed(2)); fd.append('geojson', JSON.stringify(geojsonSementara));
|
|
|
|
let btn = event.target;
|
|
let oldText = btn.innerHTML;
|
|
btn.innerHTML = "Menyimpan...";
|
|
btn.disabled = true;
|
|
|
|
fetch('simpan_jalan.php', { method: 'POST', body: fd })
|
|
.then(res => res.text()).then(res => {
|
|
if (res.includes("Sukses")) {
|
|
map.closePopup(); map.removeLayer(layerSementara); muatDataJalan();
|
|
} else { alert("Gagal: " + res); btn.innerHTML = oldText; btn.disabled = false;}
|
|
}).catch(error => { console.error('Error AJAX:', error); btn.innerHTML = oldText; btn.disabled = false;});
|
|
}
|
|
|
|
function batalGambar() {
|
|
map.removeLayer(layerSementara);
|
|
map.closePopup();
|
|
}
|
|
|
|
// ============================================================
|
|
// INIT LOAD
|
|
// ============================================================
|
|
muatDataSPBU();
|
|
muatDataJalan();
|
|
muatDataKavling();
|
|
</script>
|
|
|
|
</body>
|
|
</html> |