227 lines
12 KiB
JavaScript
227 lines
12 KiB
JavaScript
/* ============================================================
|
|
draw_spbu.js — WebGIS SPBU Pontianak
|
|
Handles: SPBU (Point), Jalan (Line), Parsil (Polygon)
|
|
============================================================ */
|
|
|
|
var map = L.map('map', { zoomControl: false }).setView([-0.0263, 109.3425], 13);
|
|
L.control.zoom({ position: 'bottomright' }).addTo(map);
|
|
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
attribution: '© OpenStreetMap contributors', maxZoom: 19
|
|
}).addTo(map);
|
|
|
|
/* ── LAYER GROUPS ── */
|
|
var drawnItems = new L.FeatureGroup().addTo(map);
|
|
var layerGroup24 = L.featureGroup().addTo(map);
|
|
var layerGroupTidak = L.featureGroup().addTo(map);
|
|
|
|
var show24 = true;
|
|
var showTidak = true;
|
|
|
|
let currentLayer = null;
|
|
|
|
/* ============================================================
|
|
TOGGLE LAYERS
|
|
============================================================ */
|
|
function toggleLayer(type) {
|
|
if (type === '24jam') {
|
|
show24 = !show24;
|
|
show24 ? map.addLayer(layerGroup24) : map.removeLayer(layerGroup24);
|
|
document.getElementById('btn24').classList.toggle('active', show24);
|
|
} else if (type === 'tidak') {
|
|
showTidak = !showTidak;
|
|
showTidak ? map.addLayer(layerGroupTidak) : map.removeLayer(layerGroupTidak);
|
|
document.getElementById('btnTidak').classList.toggle('active', showTidak);
|
|
}
|
|
}
|
|
|
|
/* ============================================================
|
|
DRAW CONTROL
|
|
============================================================ */
|
|
var drawControl = new L.Control.Draw({
|
|
position: 'topleft',
|
|
edit: { featureGroup: drawnItems },
|
|
draw: {
|
|
marker: true,
|
|
polyline: { shapeOptions: { color: '#a855f7', weight: 3 } },
|
|
polygon: { shapeOptions: { color: '#10b981', weight: 2, fillOpacity: 0.35 } },
|
|
rectangle: false, circle: false, circlemarker: false
|
|
}
|
|
});
|
|
map.addControl(drawControl);
|
|
|
|
/* ============================================================
|
|
HELPERS
|
|
============================================================ */
|
|
function getLineColor(s) {
|
|
return s==='Nasional' ? '#ef4444' : s==='Provinsi' ? '#38bdf8' : '#a855f7';
|
|
}
|
|
function getLength(ll) {
|
|
let t=0;
|
|
for(let i=0;i<ll.length-1;i++) t+=ll[i].distanceTo(ll[i+1]);
|
|
return t;
|
|
}
|
|
function fmtNum(n, dec=2) {
|
|
return parseFloat(n).toLocaleString('id-ID', { maximumFractionDigits: dec });
|
|
}
|
|
function statusBadge(s, type) {
|
|
if (type==='point') return s==='24 Jam' ? `<span class="ip-badge badge-green">24 Jam</span>` : `<span class="ip-badge badge-red">Tidak 24 Jam</span>`;
|
|
if (type==='line') { let c=s==='Nasional'?'badge-red':s==='Provinsi'?'badge-blue':'badge-gray'; return `<span class="ip-badge ${c}">${s}</span>`; }
|
|
return `<span class="ip-badge ${s==='SHM'?'badge-green':'badge-blue'}">${s}</span>`;
|
|
}
|
|
|
|
/* ── Icons ── */
|
|
function makeIcon(status) {
|
|
var c = status==='24 Jam' ? '#4ade80' : '#f87171';
|
|
return L.divIcon({ html:`<svg xmlns="http://www.w3.org/2000/svg" width="26" height="38" viewBox="0 0 26 38"><path d="M13 0C5.8 0 0 5.8 0 13c0 9.75 13 25 13 25S26 22.75 26 13C26 5.8 20.2 0 13 0z" fill="${c}" stroke="rgba(255,255,255,.25)" stroke-width="1.5"/><circle cx="13" cy="13" r="5" fill="rgba(0,0,0,.4)"/><circle cx="13" cy="13" r="3" fill="white"/></svg>`, iconSize:[26,38], iconAnchor:[13,38], popupAnchor:[0,-40], className:'' });
|
|
}
|
|
|
|
/* ============================================================
|
|
POPUP BUILDERS
|
|
============================================================ */
|
|
function buildInfoPopup(d, type) {
|
|
const hc = { point:'linear-gradient(135deg,#1d4ed8,#3b82f6)', line:'linear-gradient(135deg,#7c3aed,#a855f7)', polygon:'linear-gradient(135deg,#065f46,#10b981)' };
|
|
const ic = { point:'⛽', line:'🛣️', polygon:'📐' };
|
|
const ti = { point:`SPBU — ${d.nama}`, line:`Jalan — ${d.nama}`, polygon:`Parsil — ${d.nama}` };
|
|
|
|
let rows = '';
|
|
if (type==='point') {
|
|
rows = `<div class="ip-row"><span class="ip-label">No. SPBU</span><span class="ip-val">${d.no_spbu}</span></div>
|
|
<div class="ip-row"><span class="ip-label">Status</span><span class="ip-val">${statusBadge(d.status,'point')}</span></div>
|
|
<div class="ip-row"><span class="ip-label">Koordinat</span><span class="ip-val" style="font-size:10px">${parseFloat(d.latitude).toFixed(5)}, ${parseFloat(d.longitude).toFixed(5)}</span></div>`;
|
|
} else if (type==='line') {
|
|
rows = `<div class="ip-row"><span class="ip-label">Status</span><span class="ip-val">${statusBadge(d.status,'line')}</span></div>
|
|
<div class="ip-row"><span class="ip-label">Panjang</span><span class="ip-val">${fmtNum(d.panjang)} m</span></div>`;
|
|
} else if (type==='polygon') {
|
|
rows = `<div class="ip-row"><span class="ip-label">Status</span><span class="ip-val">${statusBadge(d.status,'polygon')}</span></div>
|
|
<div class="ip-row"><span class="ip-label">Luas</span><span class="ip-val">${fmtNum(d.luas)} m²</span></div>`;
|
|
}
|
|
|
|
return `<div class="ip"><div class="ip-head" style="background:${hc[type]}">${ic[type]} ${ti[type]}</div><div class="ip-body">${rows}<button class="ip-del" onclick="hapusData(${d.id},'${type}')">🗑 Hapus</button></div></div>`;
|
|
}
|
|
|
|
/* ============================================================
|
|
LOAD DATA
|
|
============================================================ */
|
|
fetch('../php/show_spbu.php')
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
data.forEach(d => {
|
|
if (d.type === 'point') {
|
|
var layer = L.marker([d.latitude, d.longitude], { icon: makeIcon(d.status) });
|
|
layer._type='point'; layer._db_id=d.id;
|
|
layer.bindPopup(buildInfoPopup(d,'point'), { maxWidth:260 });
|
|
d.status==='24 Jam' ? layerGroup24.addLayer(layer) : layerGroupTidak.addLayer(layer);
|
|
}
|
|
if (d.type === 'line') {
|
|
var coords = JSON.parse(d.geom);
|
|
var layer = L.polyline(coords, { color:getLineColor(d.status), weight:4, opacity:0.9 });
|
|
layer._type='line'; layer._db_id=d.id;
|
|
layer.bindPopup(buildInfoPopup(d,'line'), { maxWidth:260 });
|
|
drawnItems.addLayer(layer);
|
|
}
|
|
if (d.type === 'polygon') {
|
|
var coords = JSON.parse(d.geom);
|
|
var layer = L.polygon(coords, { color:'#10b981', weight:2, fillColor:'#10b981', fillOpacity:0.3 });
|
|
layer._type='polygon'; layer._db_id=d.id;
|
|
layer.bindPopup(buildInfoPopup(d,'polygon'), { maxWidth:260 });
|
|
drawnItems.addLayer(layer);
|
|
}
|
|
});
|
|
})
|
|
.catch(err => console.error('Gagal load data:', err));
|
|
|
|
/* ============================================================
|
|
HAPUS
|
|
============================================================ */
|
|
function hapusData(id, type) {
|
|
if (!confirm('Yakin ingin menghapus?')) return;
|
|
fetch(`../php/delete.php?id=${id}&type=${type}`)
|
|
.then(r=>r.json()).then(r=>{
|
|
if (r.message && r.message.toLowerCase().includes('berhasil')) location.reload();
|
|
else alert('Gagal: ' + (r.error||r.message));
|
|
});
|
|
}
|
|
|
|
/* ============================================================
|
|
ON CREATED
|
|
============================================================ */
|
|
map.on(L.Draw.Event.CREATED, function(e) {
|
|
var layer = e.layer;
|
|
|
|
if (e.layerType === 'marker') { drawnItems.addLayer(layer); handlePoint(layer); }
|
|
if (e.layerType === 'polyline') { drawnItems.addLayer(layer); handleLine(layer); }
|
|
if (e.layerType === 'polygon') { drawnItems.addLayer(layer); handlePolygon(layer); }
|
|
});
|
|
|
|
/* ── SPBU ── */
|
|
function handlePoint(layer) {
|
|
var ll = layer.getLatLng();
|
|
layer.bindPopup(`<div class="pf"><div class="pf-head pf-head-point">⛽ Tambah SPBU</div><div class="pf-body">
|
|
<label>Nama SPBU</label><input id="pNama" placeholder="contoh: SPBU Sungai Jawi">
|
|
<label>No. SPBU</label><input id="pNo" placeholder="contoh: 64.761.xx">
|
|
<label>Status Operasional</label>
|
|
<select id="pStatus"><option value="24 Jam">24 Jam</option><option value="Tidak">Tidak 24 Jam</option></select>
|
|
<div class="pf-info">📍 ${ll.lat.toFixed(6)}, ${ll.lng.toFixed(6)}</div>
|
|
<button class="pf-btn" onclick="savePoint(${ll.lat},${ll.lng})">💾 Simpan</button>
|
|
</div></div>`, { maxWidth:280 }).openPopup();
|
|
}
|
|
function savePoint(lat, lng) {
|
|
var nama=document.getElementById('pNama').value.trim(), no=document.getElementById('pNo').value.trim(), status=document.getElementById('pStatus').value;
|
|
if (!nama||!no) { alert('Nama dan No. SPBU wajib diisi!'); return; }
|
|
fetch('../php/insert.php',{ method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({nama,no,status,lat,lng}) })
|
|
.then(r=>r.json()).then(r=>{ if(r.message&&r.message.includes('berhasil')) location.reload(); else alert('Gagal: '+r.message); });
|
|
}
|
|
|
|
/* ── LINE ── */
|
|
function handleLine(layer) {
|
|
currentLayer=layer;
|
|
var ll=layer.getLatLngs(), p=getLength(ll); layer._temp={latlngs:ll,panjang:p};
|
|
L.popup({maxWidth:280}).setLatLng(layer.getBounds().getCenter()).setContent(`<div class="pf"><div class="pf-head pf-head-line">🛣️ Tambah Jalan</div><div class="pf-body">
|
|
<label>Nama Jalan</label><input id="lNama" placeholder="contoh: Jl. Ahmad Yani">
|
|
<label>Klasifikasi</label><select id="lStatus"><option value="Nasional">Nasional</option><option value="Provinsi">Provinsi</option><option value="Kabupaten">Kabupaten</option></select>
|
|
<div class="pf-info">📏 ${fmtNum(p)} m</div>
|
|
<button class="pf-btn" onclick="saveLine()">💾 Simpan</button>
|
|
</div></div>`).openOn(map);
|
|
}
|
|
function saveLine() {
|
|
var layer=currentLayer; if(!layer){alert('Layer tidak ditemukan');return;}
|
|
var nama=document.getElementById('lNama').value.trim(), status=document.getElementById('lStatus').value;
|
|
if(!nama){alert('Nama wajib diisi!');return;}
|
|
fetch('../php/insert_line.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({nama,status,panjang:layer._temp.panjang,geom:JSON.stringify(layer._temp.latlngs)})})
|
|
.then(r=>r.json()).then(r=>{ if(r.id){layer._db_id=r.id;layer._type='line';map.closePopup();alert('Berhasil!');} else alert('Gagal: '+(r.error||r.message)); });
|
|
}
|
|
|
|
/* ── POLYGON ── */
|
|
function handlePolygon(layer) {
|
|
currentLayer=layer;
|
|
var ll=layer.getLatLngs(), luas=L.GeometryUtil.geodesicArea(ll[0]); layer._temp={latlngs:ll,luas};
|
|
L.popup({maxWidth:280}).setLatLng(layer.getBounds().getCenter()).setContent(`<div class="pf"><div class="pf-head pf-head-polygon">📐 Tambah Parsil</div><div class="pf-body">
|
|
<label>Nama Area</label><input id="gNama" placeholder="contoh: Lahan A">
|
|
<label>Status Kepemilikan</label><select id="gStatus"><option value="SHM">SHM</option><option value="HGB">HGB</option><option value="HGU">HGU</option><option value="HP">HP</option></select>
|
|
<div class="pf-info">📐 ${fmtNum(luas)} m²</div>
|
|
<button class="pf-btn" onclick="savePolygon()">💾 Simpan</button>
|
|
</div></div>`).openOn(map);
|
|
}
|
|
function savePolygon() {
|
|
var layer=currentLayer; if(!layer){alert('Layer tidak ditemukan');return;}
|
|
var nama=document.getElementById('gNama').value.trim(), status=document.getElementById('gStatus').value;
|
|
if(!nama){alert('Nama wajib diisi!');return;}
|
|
fetch('../php/insert_polygon.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({nama,status,luas:layer._temp.luas,geom:JSON.stringify(layer._temp.latlngs)})})
|
|
.then(r=>r.json()).then(r=>{ if(r.id){layer._db_id=r.id;layer._type='polygon';map.closePopup();alert('Berhasil!');} else alert('Gagal: '+(r.error||r.message)); });
|
|
}
|
|
|
|
/* ── EDIT & DELETE ── */
|
|
map.on('draw:edited', function(e) {
|
|
e.layers.eachLayer(layer => {
|
|
if (!layer._db_id) return;
|
|
if (layer._type==='line') fetch('../php/update_line.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({id:layer._db_id,geom:JSON.stringify(layer.getLatLngs()),panjang:getLength(layer.getLatLngs())})});
|
|
if (layer._type==='polygon') fetch('../php/update_polygon.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({id:layer._db_id,geom:JSON.stringify(layer.getLatLngs()),luas:L.GeometryUtil.geodesicArea(layer.getLatLngs()[0])})});
|
|
});
|
|
});
|
|
map.on('draw:deleted', function(e) {
|
|
e.layers.eachLayer(layer => {
|
|
if (layer._db_id&&layer._type) fetch(`../php/delete.php?id=${layer._db_id}&type=${layer._type}`);
|
|
});
|
|
});
|