Files
2026-06-13 10:54:54 +07:00

292 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<base target="_top">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>WebGIS - Jalan & Kavling</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<style>
html, body { height: 100%; margin: 0; }
#map { height: 100%; }
#toolbar {
position: absolute;
z-index: 1000;
top: 10px;
left: 50px;
background: white;
padding: 8px 12px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
display: flex;
gap: 6px;
}
#toolbar button {
padding: 6px 12px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
background: #f0f0f0;
}
#toolbar button:hover { background: #ddd; }
#toolbar button.active { background: #007bff; color: white; }
</style>
</head>
<body>
<div id="toolbar">
<button id="btnLine" onclick="startLine()">✏️ Line</button>
<button id="btnPoly" onclick="startPolygons()">🔺 Polygon</button>
<button onclick="finishShape()">✅ Selesai</button>
<button onclick="resetShape()">❌ Reset</button>
</div>
<div id="map"></div>
<script>
const map = L.map('map').setView([-0.055326, 109.349500], 13);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
let mode = null;
let points = [];
let tempLayer = null;
// ========== DRAWING ==========
map.on('click', function(e) {
if (!mode) return;
points.push([e.latlng.lat, e.latlng.lng]);
if (tempLayer) map.removeLayer(tempLayer);
if (mode === 'line') {
tempLayer = L.polyline(points, { color: 'blue', dashArray: '5,5' }).addTo(map);
} else if (mode === 'polygons') {
tempLayer = L.polygon(points, { color: 'green', dashArray: '5,5' }).addTo(map);
}
});
function startLine() {
mode = 'line';
points = [];
document.getElementById('btnLine').classList.add('active');
document.getElementById('btnPoly').classList.remove('active');
alert("Mode LINE aktif. Klik di peta untuk menambah titik.");
}
function startPolygons() {
mode = 'polygons';
points = [];
document.getElementById('btnPoly').classList.add('active');
document.getElementById('btnLine').classList.remove('active');
alert("Mode POLYGON aktif. Klik di peta untuk menambah titik.");
}
function resetShape() {
mode = null;
points = [];
if (tempLayer) { map.removeLayer(tempLayer); tempLayer = null; }
document.getElementById('btnLine').classList.remove('active');
document.getElementById('btnPoly').classList.remove('active');
}
function finishShape() {
if (!mode) { alert("Pilih mode Line atau Polygon terlebih dahulu!"); return; }
if (points.length < 2) { alert("Titik belum cukup!"); return; }
let currentType = mode;
let savedPoints = [...points];
let shape;
if (currentType === 'line') {
shape = L.polyline(savedPoints, { color: 'blue', weight: 4 }).addTo(map);
} else {
shape = L.polygon(savedPoints, { color: 'green', fillOpacity: 0.4 }).addTo(map);
}
fetch(currentType === 'line' ? 'simpan_line.php' : 'simpan_polygons.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ coordinates: savedPoints })
})
.then(res => res.json())
.then(res => {
let id = res.id;
if (currentType === 'line') {
let panjang = hitungPanjang(savedPoints);
shape.bindPopup(popupLine(id, '', panjang)).openPopup();
} else {
let luas = hitungLuas(savedPoints);
shape.bindPopup(popupPolygon(id, '', luas)).openPopup();
}
});
// Reset
mode = null; points = [];
if (tempLayer) { map.removeLayer(tempLayer); tempLayer = null; }
document.getElementById('btnLine').classList.remove('active');
document.getElementById('btnPoly').classList.remove('active');
}
// ========== POPUP ==========
function popupLine(id, status, panjang) {
return `
<div style="font-family:Arial;width:200px;">
<b>🛣️ Data Jalan</b><br><br>
<label style="font-size:12px;">Status Jalan:</label>
<select id="status_${id}" style="width:100%;padding:4px;margin:4px 0 8px;">
<option value="Nasional" ${status==='Nasional'?'selected':''}>Nasional</option>
<option value="Provinsi" ${status==='Provinsi'?'selected':''}>Provinsi</option>
<option value="Kabupaten" ${status==='Kabupaten'?'selected':''}>Kabupaten</option>
</select>
<div style="font-size:12px;margin-bottom:8px;">📏 Panjang: <b>${panjang} m</b></div>
<button onclick="updateLine(${id})" style="width:100%;padding:5px;background:#007bff;color:white;border:none;border-radius:5px;cursor:pointer;margin-bottom:4px;">✅ Update</button>
<button onclick="hapusShape(${id},'line')" style="width:100%;padding:5px;background:#dc3545;color:white;border:none;border-radius:5px;cursor:pointer;">🗑️ Hapus</button>
</div>`;
}
function popupPolygon(id, status, luas) {
return `
<div style="font-family:Arial;width:200px;">
<b>🏘️ Data Kavling</b><br><br>
<label style="font-size:12px;">Status Lahan:</label>
<select id="status_poly_${id}" style="width:100%;padding:4px;margin:4px 0 8px;">
<option value="SHM" ${status==='SHM'?'selected':''}>SHM</option>
<option value="HGB" ${status==='HGB'?'selected':''}>HGB</option>
<option value="HGU" ${status==='HGU'?'selected':''}>HGU</option>
<option value="HP" ${status==='HP' ?'selected':''}>HP</option>
</select>
<div style="font-size:12px;margin-bottom:8px;">📐 Luas: <b>${luas} m²</b></div>
<button onclick="updatePolygon(${id})" style="width:100%;padding:5px;background:#007bff;color:white;border:none;border-radius:5px;cursor:pointer;margin-bottom:4px;">✅ Update</button>
<button onclick="hapusShape(${id},'polygons')" style="width:100%;padding:5px;background:#dc3545;color:white;border:none;border-radius:5px;cursor:pointer;">🗑️ Hapus</button>
</div>`;
}
// ========== LOAD DATA ==========
function loadLines() {
fetch('get_line.php')
.then(res => res.json())
.then(data => {
data.forEach(d => {
let coords = JSON.parse(d.coordinates);
let color = getColorLine(d.status) || 'blue';
let panjang = hitungPanjang(coords);
L.polyline(coords, { color, weight: 4 }).addTo(map)
.bindPopup(popupLine(d.id, d.status, panjang));
});
});
}
function loadPolygons() {
fetch('get_polygons.php')
.then(res => res.json())
.then(data => {
data.forEach(d => {
let coords = normalizeCoords(JSON.parse(d.coordinates));
if (!Array.isArray(coords) || coords.length < 3) return;
let color = getColorPolygon(d.status) || 'green';
let luas = hitungLuas(coords);
L.polygon(coords, { color, fillOpacity: 0.4 }).addTo(map)
.bindPopup(popupPolygon(d.id, d.status, luas));
});
});
}
loadLines();
loadPolygons();
// ========== CRUD ==========
function hapusShape(id, type) {
if (!confirm("Yakin ingin menghapus?")) return;
fetch(type === 'line' ? 'delete_line.php' : 'delete_polygons.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id })
}).then(() => location.reload());
}
function updateLine(id) {
let status = document.getElementById('status_' + id).value;
fetch('get_line.php').then(r => r.json()).then(data => {
let coords = JSON.parse(data.find(d => d.id == id).coordinates);
let panjang = hitungPanjang(coords);
fetch('update_line.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id, status, panjang })
}).then(() => { alert("Data jalan berhasil diupdate!"); location.reload(); });
});
}
function updatePolygon(id) {
let status = document.getElementById('status_poly_' + id).value;
fetch('get_polygons.php').then(r => r.json()).then(data => {
let coords = normalizeCoords(JSON.parse(data.find(d => d.id == id).coordinates));
let luas = hitungLuas(coords);
fetch('update_polygons.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id, status, luas })
}).then(() => { alert("Status kavling berhasil diupdate!"); location.reload(); });
});
}
// ========== HELPERS ==========
function normalizeCoords(raw) {
if (raw && raw.type === 'Polygon' && raw.coordinates) return raw.coordinates[0].map(p => [p[1], p[0]]);
if (raw && raw.type === 'LineString' && raw.coordinates) return raw.coordinates.map(p => [p[1], p[0]]);
if (Array.isArray(raw)) return raw;
return raw;
}
function getColorLine(status) {
if (status === 'Nasional') return 'red';
if (status === 'Provinsi') return 'blue';
if (status === 'Kabupaten') return 'green';
return 'gray';
}
function getColorPolygon(status) {
if (status === 'SHM') return 'green';
if (status === 'HGB') return 'blue';
if (status === 'HGU') return 'orange';
if (status === 'HP') return 'purple';
return 'gray';
}
function hitungPanjang(points) {
let total = 0;
for (let i = 0; i < points.length - 1; i++) {
total += L.latLng(points[i]).distanceTo(L.latLng(points[i + 1]));
}
return total.toFixed(2);
}
function hitungLuas(points) {
if (!Array.isArray(points) || points.length < 3) return 0;
let latlngs = points.map(p => Array.isArray(p) ? L.latLng(p[0], p[1]) : L.latLng(p.lat, p.lng));
const R = 6371000;
let area = 0, n = latlngs.length;
for (let i = 0; i < n; i++) {
let j = (i + 1) % n;
let xi = latlngs[i].lng * Math.PI / 180, yi = latlngs[i].lat * Math.PI / 180;
let xj = latlngs[j].lng * Math.PI / 180, yj = latlngs[j].lat * Math.PI / 180;
area += (xj - xi) * (2 + Math.sin(yi) + Math.sin(yj));
}
return (Math.abs(area) * R * R / 2).toFixed(2);
}
</script>
</body>
</html>