409 lines
14 KiB
PHP
409 lines
14 KiB
PHP
<?php
|
|
// Data diambil dari API - tidak ada koneksi MySQL di frontend ini
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="id">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>WebGIS Manajemen Data Jalan</title>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
font-family: Arial, sans-serif;
|
|
background: #f4f7fb;
|
|
color: #333;
|
|
}
|
|
header {
|
|
background: #1f2937;
|
|
color: #fff;
|
|
padding: 18px 24px;
|
|
text-align: center;
|
|
}
|
|
main {
|
|
max-width: 1160px;
|
|
margin: 24px auto;
|
|
padding: 0 16px 32px;
|
|
}
|
|
#map {
|
|
width: 100% !important;
|
|
height: 520px !important;
|
|
display: block !important;
|
|
position: relative !important;
|
|
visibility: visible !important;
|
|
border: 1px solid #cbd5e1;
|
|
border-radius: 12px;
|
|
box-shadow: 0 10px 24px rgba(15,23,42,.08);
|
|
}
|
|
.panel {
|
|
margin-top: 24px;
|
|
display: grid;
|
|
gap: 18px;
|
|
}
|
|
.card {
|
|
background: #fff;
|
|
border-radius: 14px;
|
|
padding: 20px;
|
|
box-shadow: 0 8px 20px rgba(15,23,42,.06);
|
|
}
|
|
.card h2 {
|
|
margin: 0 0 14px;
|
|
font-size: 1.2rem;
|
|
}
|
|
.form-group {
|
|
margin-bottom: 14px;
|
|
}
|
|
.form-group label {
|
|
display: block;
|
|
margin-bottom: 6px;
|
|
font-weight: 600;
|
|
}
|
|
.form-group input,
|
|
.form-group select,
|
|
.form-group textarea {
|
|
width: 100%;
|
|
padding: 10px 12px;
|
|
border: 1px solid #cbd5e1;
|
|
border-radius: 10px;
|
|
font-size: 0.98rem;
|
|
background: #f8fafc;
|
|
}
|
|
.form-group input[readonly] {
|
|
background: #e2e8f0;
|
|
cursor: not-allowed;
|
|
}
|
|
.button-row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 12px;
|
|
align-items: center;
|
|
}
|
|
.btn {
|
|
display: inline-flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 12px 18px;
|
|
border: none;
|
|
border-radius: 10px;
|
|
font-size: 1rem;
|
|
cursor: pointer;
|
|
transition: background 0.2s ease;
|
|
}
|
|
.btn-primary {
|
|
background: #2563eb;
|
|
color: #fff;
|
|
}
|
|
.btn-primary:hover {
|
|
background: #1d4ed8;
|
|
}
|
|
.btn-secondary {
|
|
background: #64748b;
|
|
color: #fff;
|
|
}
|
|
.btn-secondary:hover {
|
|
background: #475569;
|
|
}
|
|
.alert {
|
|
padding: 14px 16px;
|
|
border-radius: 10px;
|
|
border: 1px solid transparent;
|
|
}
|
|
.alert-error {
|
|
background: #fee2e2;
|
|
border-color: #fca5a5;
|
|
color: #b91c1c;
|
|
}
|
|
.alert-success {
|
|
background: #dcfce7;
|
|
border-color: #a7f3d0;
|
|
color: #166534;
|
|
}
|
|
.note {
|
|
font-size: 0.95rem;
|
|
color: #475569;
|
|
}
|
|
.road-list {
|
|
display: grid;
|
|
gap: 12px;
|
|
}
|
|
.road-item {
|
|
border: 1px solid #e2e8f0;
|
|
padding: 14px 16px;
|
|
border-radius: 12px;
|
|
background: #f8fafc;
|
|
}
|
|
.road-item strong {
|
|
display: block;
|
|
margin-bottom: 6px;
|
|
font-size: 1rem;
|
|
}
|
|
.meta {
|
|
color: #475569;
|
|
font-size: 0.95rem;
|
|
line-height: 1.5;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<h1>🛣️ WebGIS Manajemen Data Jalan</h1>
|
|
<p>Tambah, simpan, dan tampilkan polyline jalan dengan LeafletJS (API Mode)</p>
|
|
</header>
|
|
<main>
|
|
<section class="card">
|
|
<h2>Petunjuk</h2>
|
|
<p class="note">Klik peta berurutan untuk membuat garis jalan sementara. Sistem akan menghitung panjang otomatis dan menyimpan koordinat JSON ke database.</p>
|
|
</section>
|
|
|
|
<section class="card">
|
|
<div id="map"></div>
|
|
</section>
|
|
|
|
<?php if (!empty($errors)): ?>
|
|
<section class="alert alert-error card">
|
|
<strong>Terjadi kesalahan:</strong>
|
|
<ul>
|
|
<?php foreach ($errors as $error): ?>
|
|
<li><?= htmlspecialchars($error) ?></li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</section>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($success): ?>
|
|
<section class="alert alert-success card">
|
|
<?= htmlspecialchars($success) ?>
|
|
</section>
|
|
<?php endif; ?>
|
|
|
|
<section class="card">
|
|
<h2>Form Tambah Jalan</h2>
|
|
<form method="post" action="<?= htmlspecialchars($_SERVER['PHP_SELF']) ?>">
|
|
<div class="form-group">
|
|
<label for="nama_jalan">Nama Jalan</label>
|
|
<input type="text" id="nama_jalan" name="nama_jalan" required value="<?= htmlspecialchars($input['nama_jalan']) ?>" placeholder="e.g. Jalan Ahmad Yani">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="status_jalan">Status Jalan</label>
|
|
<select id="status_jalan" name="status_jalan" required>
|
|
<option value="">Pilih status jalan</option>
|
|
<option value="nasional" <?= $input['status_jalan'] === 'nasional' ? 'selected' : '' ?>>nasional</option>
|
|
<option value="provinsi" <?= $input['status_jalan'] === 'provinsi' ? 'selected' : '' ?>>provinsi</option>
|
|
<option value="kabupaten" <?= $input['status_jalan'] === 'kabupaten' ? 'selected' : '' ?>>kabupaten</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Panjang Jalan (otomatis)</label>
|
|
<input type="text" id="panjang_tampil" readonly value="0 meter">
|
|
<input type="hidden" name="panjang_meter" id="panjang_meter" value="">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Koordinat JSON (otomatis)</label>
|
|
<textarea readonly rows="3" id="koordinat_tampil" style="resize: vertical; background: #e2e8f0;"></textarea>
|
|
<input type="hidden" name="koordinat_json" id="koordinat_json" value="">
|
|
</div>
|
|
<div class="button-row">
|
|
<button type="button" class="btn btn-secondary" id="reset-button">Reset Garis</button>
|
|
<button type="submit" class="btn btn-primary">Simpan Data Jalan</button>
|
|
</div>
|
|
</form>
|
|
</section>
|
|
|
|
<section class="card">
|
|
<h2>Daftar Jalan Tersimpan</h2>
|
|
<div class="road-list" id="road-list-container">
|
|
<div class="road-item">Memuat data dari API...</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
|
<script>
|
|
// Load existing roads from API
|
|
let existingRoads = [];
|
|
|
|
function loadRoadsFromAPI() {
|
|
fetch('api/get_roads.php')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success && Array.isArray(data.roads)) {
|
|
existingRoads = data.roads;
|
|
renderExistingRoads();
|
|
}
|
|
})
|
|
.catch(error => console.error('Error loading roads:', error));
|
|
}
|
|
|
|
function renderExistingRoads() {
|
|
const statusColors = {
|
|
nasional: '#DC3545',
|
|
provinsi: '#007BFF',
|
|
kabupaten: '#28A745'
|
|
};
|
|
|
|
// Clear existing layers
|
|
map.eachLayer(layer => {
|
|
if (layer instanceof L.Polyline && !layer.hasOwnProperty('_userDrawn')) {
|
|
map.removeLayer(layer);
|
|
}
|
|
});
|
|
|
|
// Render roads on map
|
|
existingRoads.forEach(road => {
|
|
const coords = typeof road.koordinat === 'string' ? JSON.parse(road.koordinat) : road.koordinat;
|
|
if (!Array.isArray(coords) || coords.length < 2) return;
|
|
|
|
const line = L.polyline(coords.map(([lat, lng]) => [lat, lng]), {
|
|
color: statusColors[road.status_jalan] || '#444',
|
|
weight: 6,
|
|
opacity: 0.8,
|
|
smoothFactor: 1
|
|
}).addTo(map);
|
|
|
|
line.bindPopup(`
|
|
<strong>${road.nama_jalan}</strong><br>
|
|
Status: ${road.status_jalan}<br>
|
|
Panjang: ${road.panjang_meter} meter
|
|
`);
|
|
});
|
|
|
|
// Update list display
|
|
const listContainer = document.getElementById('road-list-container');
|
|
if (existingRoads.length === 0) {
|
|
listContainer.innerHTML = '<div class="road-item">Belum ada data jalan yang tersimpan.</div>';
|
|
} else {
|
|
listContainer.innerHTML = existingRoads.map(road => `
|
|
<div class="road-item">
|
|
<strong>${road.nama_jalan}</strong>
|
|
<div class="meta">Status: ${road.status_jalan} | Panjang: ${road.panjang_meter} meter</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
}
|
|
|
|
// Initialize map
|
|
const map = L.map('map').setView([-0.0263, 109.3425], 13);
|
|
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
maxZoom: 19,
|
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
|
}).addTo(map);
|
|
|
|
// Load existing roads on page load
|
|
loadRoadsFromAPI();
|
|
|
|
const statusColors = {
|
|
nasional: '#DC3545',
|
|
provinsi: '#007BFF',
|
|
kabupaten: '#28A745'
|
|
};
|
|
|
|
let currentPoints = [];
|
|
let currentLine = null;
|
|
|
|
const panjangMeterInput = document.getElementById('panjang_meter');
|
|
const panjangTampil = document.getElementById('panjang_tampil');
|
|
const koordinatInput = document.getElementById('koordinat_json');
|
|
const koordinatTampil = document.getElementById('koordinat_tampil');
|
|
const resetButton = document.getElementById('reset-button');
|
|
|
|
map.on('click', function (event) {
|
|
currentPoints.push(event.latlng);
|
|
updateTemporaryPolyline();
|
|
updateFormFields();
|
|
});
|
|
|
|
resetButton.addEventListener('click', function () {
|
|
currentPoints = [];
|
|
if (currentLine) {
|
|
map.removeLayer(currentLine);
|
|
currentLine = null;
|
|
}
|
|
updateFormFields();
|
|
});
|
|
|
|
function updateTemporaryPolyline() {
|
|
if (currentLine) {
|
|
map.removeLayer(currentLine);
|
|
}
|
|
if (currentPoints.length >= 2) {
|
|
currentLine = L.polyline(currentPoints, {
|
|
color: '#0f172a',
|
|
weight: 5,
|
|
dashArray: '8, 8',
|
|
opacity: 0.85
|
|
}).addTo(map);
|
|
currentLine._userDrawn = true;
|
|
} else if (currentPoints.length === 1) {
|
|
currentLine = L.circleMarker(currentPoints[0], {
|
|
radius: 6,
|
|
fillColor: '#0f172a',
|
|
color: '#ffffff',
|
|
weight: 2,
|
|
fillOpacity: 1
|
|
}).addTo(map);
|
|
currentLine._userDrawn = true;
|
|
}
|
|
}
|
|
|
|
function updateFormFields() {
|
|
let totalDistance = 0;
|
|
for (let i = 1; i < currentPoints.length; i++) {
|
|
totalDistance += currentPoints[i - 1].distanceTo(currentPoints[i]);
|
|
}
|
|
const roundedDistance = Math.round(totalDistance);
|
|
panjangMeterInput.value = roundedDistance;
|
|
panjangTampil.value = `${roundedDistance} meter`;
|
|
|
|
const coordsArray = currentPoints.map(point => [point.lat, point.lng]);
|
|
koordinatInput.value = JSON.stringify(coordsArray);
|
|
koordinatTampil.value = koordinatInput.value;
|
|
}
|
|
|
|
updateFormFields();
|
|
|
|
// Handle form submission via API
|
|
document.querySelector('form').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
const formData = {
|
|
nama_jalan: document.getElementById('nama_jalan').value,
|
|
status_jalan: document.getElementById('status_jalan').value,
|
|
panjang_meter: panjangMeterInput.value,
|
|
koordinat_json: koordinatInput.value
|
|
};
|
|
|
|
if (!formData.nama_jalan || !formData.status_jalan || !formData.panjang_meter || !formData.koordinat_json) {
|
|
alert('Semua field harus diisi dan polyline harus dibuat sebelum submit');
|
|
return;
|
|
}
|
|
|
|
fetch('api/save_road.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams(formData)
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('Data jalan berhasil disimpan!');
|
|
currentPoints = [];
|
|
if (currentLine) map.removeLayer(currentLine);
|
|
updateFormFields();
|
|
loadRoadsFromAPI();
|
|
this.reset();
|
|
} else {
|
|
alert('Error: ' + (data.message || 'Gagal menyimpan data'));
|
|
}
|
|
})
|
|
.catch(error => alert('Error: ' + error));
|
|
});
|
|
|
|
window.addEventListener('load', function () {
|
|
map.invalidateSize();
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|