Compare commits
4 Commits
8b9dc463a8
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 422c6cb496 | |||
| 61b1879377 | |||
| 43e99a0f54 | |||
| 94d54d9521 |
+114
-142
@@ -1,102 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
// Data diambil dari API - tidak ada koneksi MySQL di frontend ini
|
||||||
* index.php
|
|
||||||
* WebGIS Manajemen Data Jalan (Polyline) - PHP Native + MySQL + LeafletJS
|
|
||||||
*
|
|
||||||
* Contoh SQL untuk membuat database / tabel dan data sampel:
|
|
||||||
*
|
|
||||||
* CREATE DATABASE IF NOT EXISTS db_webgis CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
* USE db_webgis;
|
|
||||||
*
|
|
||||||
* CREATE TABLE IF NOT EXISTS t_jalan (
|
|
||||||
* id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
* nama_jalan VARCHAR(255) NOT NULL,
|
|
||||||
* status_jalan ENUM('nasional','provinsi','kabupaten') NOT NULL,
|
|
||||||
* panjang_meter DECIMAL(10,2) NOT NULL,
|
|
||||||
* koordinat_json LONGTEXT NOT NULL,
|
|
||||||
* created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
* );
|
|
||||||
*
|
|
||||||
* INSERT INTO t_jalan (nama_jalan, status_jalan, panjang_meter, koordinat_json) VALUES
|
|
||||||
* ('Jalan Ahmad Yani', 'nasional', 3200, '[[-0.0251,109.3292],[-0.0266,109.3338],[-0.0263,109.3425]]'),
|
|
||||||
* ('Jalan Supadio', 'provinsi', 2400, '[[-0.0300,109.3270],[-0.0285,109.3335],[-0.0262,109.3420]]'),
|
|
||||||
* ('Jalan Gajah Mada', 'kabupaten', 1800, '[[-0.0288,109.3389],[-0.0274,109.3452],[-0.0252,109.3501]]');
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Konfigurasi koneksi MySQL: sesuaikan dengan kredensial lokal Anda.
|
|
||||||
$dbHost = 'localhost';
|
|
||||||
$dbName = 'webgis_spbu';
|
|
||||||
$dbUser = 'root';
|
|
||||||
$dbPass = '';
|
|
||||||
|
|
||||||
$dsn = "mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4";
|
|
||||||
$options = [
|
|
||||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
||||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
||||||
PDO::ATTR_EMULATE_PREPARES => false,
|
|
||||||
];
|
|
||||||
|
|
||||||
$errors = [];
|
|
||||||
$success = '';
|
|
||||||
$input = [
|
|
||||||
'nama_jalan' => '',
|
|
||||||
'status_jalan' => '',
|
|
||||||
'panjang_meter' => '',
|
|
||||||
'koordinat_json' => '',
|
|
||||||
];
|
|
||||||
|
|
||||||
try {
|
|
||||||
$pdo = new PDO($dsn, $dbUser, $dbPass, $options);
|
|
||||||
|
|
||||||
// Pastikan tabel t_jalan ada.
|
|
||||||
$pdo->exec("CREATE TABLE IF NOT EXISTS t_jalan (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
nama_jalan VARCHAR(255) NOT NULL,
|
|
||||||
status_jalan ENUM('nasional','provinsi','kabupaten') NOT NULL,
|
|
||||||
panjang_meter DECIMAL(10,2) NOT NULL,
|
|
||||||
koordinat_json LONGTEXT NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
$input['nama_jalan'] = trim($_POST['nama_jalan'] ?? '');
|
|
||||||
$input['status_jalan'] = $_POST['status_jalan'] ?? '';
|
|
||||||
$input['panjang_meter'] = $_POST['panjang_meter'] ?? '';
|
|
||||||
$input['koordinat_json'] = $_POST['koordinat_json'] ?? '';
|
|
||||||
|
|
||||||
if ($input['nama_jalan'] === '') {
|
|
||||||
$errors[] = 'Nama Jalan harus diisi.';
|
|
||||||
}
|
|
||||||
if (!in_array($input['status_jalan'], ['nasional', 'provinsi', 'kabupaten'], true)) {
|
|
||||||
$errors[] = 'Status Jalan tidak valid.';
|
|
||||||
}
|
|
||||||
if ($input['panjang_meter'] === '' || !is_numeric($input['panjang_meter']) || (float)$input['panjang_meter'] <= 0) {
|
|
||||||
$errors[] = 'Panjang Jalan harus dihitung otomatis dari peta dan tidak boleh kosong.';
|
|
||||||
}
|
|
||||||
$coords = json_decode($input['koordinat_json'], true);
|
|
||||||
if (!is_array($coords) || count($coords) < 2) {
|
|
||||||
$errors[] = 'Koordinat JSON tidak valid: minimal 2 titik diperlukan.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($errors)) {
|
|
||||||
$stmt = $pdo->prepare('INSERT INTO t_jalan (nama_jalan, status_jalan, panjang_meter, koordinat_json) VALUES (:nama, :status, :panjang, :koordinat)');
|
|
||||||
$stmt->execute([
|
|
||||||
':nama' => $input['nama_jalan'],
|
|
||||||
':status' => $input['status_jalan'],
|
|
||||||
':panjang' => (float)$input['panjang_meter'],
|
|
||||||
':koordinat' => json_encode($coords, JSON_UNESCAPED_UNICODE),
|
|
||||||
]);
|
|
||||||
$success = 'Data jalan berhasil disimpan ke database.';
|
|
||||||
$input = ['nama_jalan' => '', 'status_jalan' => '', 'panjang_meter' => '', 'koordinat_json' => ''];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt = $pdo->query('SELECT id, nama_jalan, status_jalan, panjang_meter, koordinat_json FROM t_jalan ORDER BY id DESC');
|
|
||||||
$jalanList = $stmt->fetchAll();
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
$errors[] = 'Koneksi database gagal: ' . htmlspecialchars($e->getMessage());
|
|
||||||
$jalanList = [];
|
|
||||||
}
|
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="id">
|
<html lang="id">
|
||||||
@@ -244,8 +147,8 @@ try {
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<h1>WebGIS Manajemen Data Jalan</h1>
|
<h1>🛣️ WebGIS Manajemen Data Jalan</h1>
|
||||||
<p>Tambah, simpan, dan tampilkan polyline jalan dengan LeafletJS dan MySQL.</p>
|
<p>Tambah, simpan, dan tampilkan polyline jalan dengan LeafletJS (API Mode)</p>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<section class="card">
|
<section class="card">
|
||||||
@@ -292,13 +195,13 @@ try {
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Panjang Jalan (otomatis)</label>
|
<label>Panjang Jalan (otomatis)</label>
|
||||||
<input type="text" id="panjang_tampil" readonly value="<?= $input['panjang_meter'] !== '' ? htmlspecialchars($input['panjang_meter']) . ' meter' : '0 meter' ?>">
|
<input type="text" id="panjang_tampil" readonly value="0 meter">
|
||||||
<input type="hidden" name="panjang_meter" id="panjang_meter" value="<?= htmlspecialchars($input['panjang_meter']) ?>">
|
<input type="hidden" name="panjang_meter" id="panjang_meter" value="">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Koordinat JSON (otomatis)</label>
|
<label>Koordinat JSON (otomatis)</label>
|
||||||
<textarea readonly rows="3" id="koordinat_tampil" style="resize: vertical; background: #e2e8f0;"><?= htmlspecialchars($input['koordinat_json']) ?></textarea>
|
<textarea readonly rows="3" id="koordinat_tampil" style="resize: vertical; background: #e2e8f0;"></textarea>
|
||||||
<input type="hidden" name="koordinat_json" id="koordinat_json" value="<?= htmlspecialchars($input['koordinat_json']) ?>">
|
<input type="hidden" name="koordinat_json" id="koordinat_json" value="">
|
||||||
</div>
|
</div>
|
||||||
<div class="button-row">
|
<div class="button-row">
|
||||||
<button type="button" class="btn btn-secondary" id="reset-button">Reset Garis</button>
|
<button type="button" class="btn btn-secondary" id="reset-button">Reset Garis</button>
|
||||||
@@ -309,23 +212,77 @@ try {
|
|||||||
|
|
||||||
<section class="card">
|
<section class="card">
|
||||||
<h2>Daftar Jalan Tersimpan</h2>
|
<h2>Daftar Jalan Tersimpan</h2>
|
||||||
<div class="road-list">
|
<div class="road-list" id="road-list-container">
|
||||||
<?php if (count($jalanList) === 0): ?>
|
<div class="road-item">Memuat data dari API...</div>
|
||||||
<div class="road-item">Belum ada data jalan yang tersimpan.</div>
|
|
||||||
<?php else: ?>
|
|
||||||
<?php foreach ($jalanList as $jalan): ?>
|
|
||||||
<div class="road-item">
|
|
||||||
<strong><?= htmlspecialchars($jalan['nama_jalan']) ?></strong>
|
|
||||||
<div class="meta">Status: <?= htmlspecialchars($jalan['status_jalan']) ?> | Panjang: <?= htmlspecialchars($jalan['panjang_meter']) ?> meter</div>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
<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);
|
const map = L.map('map').setView([-0.0263, 109.3425], 13);
|
||||||
|
|
||||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
@@ -333,39 +290,15 @@ try {
|
|||||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
|
||||||
|
// Load existing roads on page load
|
||||||
|
loadRoadsFromAPI();
|
||||||
|
|
||||||
const statusColors = {
|
const statusColors = {
|
||||||
nasional: '#DC3545',
|
nasional: '#DC3545',
|
||||||
provinsi: '#007BFF',
|
provinsi: '#007BFF',
|
||||||
kabupaten: '#28A745'
|
kabupaten: '#28A745'
|
||||||
};
|
};
|
||||||
|
|
||||||
const jalanData = <?= json_encode(array_map(function ($row) {
|
|
||||||
return [
|
|
||||||
'id' => $row['id'],
|
|
||||||
'nama_jalan' => $row['nama_jalan'],
|
|
||||||
'status_jalan' => $row['status_jalan'],
|
|
||||||
'panjang_meter' => $row['panjang_meter'],
|
|
||||||
'koordinat' => json_decode($row['koordinat_json'], true)
|
|
||||||
];
|
|
||||||
}, $jalanList), JSON_UNESCAPED_UNICODE) ?>;
|
|
||||||
|
|
||||||
jalanData.forEach(jalan => {
|
|
||||||
if (!Array.isArray(jalan.koordinat) || jalan.koordinat.length < 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const line = L.polyline(jalan.koordinat.map(([lat, lng]) => [lat, lng]), {
|
|
||||||
color: statusColors[jalan.status_jalan] || '#444',
|
|
||||||
weight: 6,
|
|
||||||
opacity: 0.8,
|
|
||||||
smoothFactor: 1
|
|
||||||
}).addTo(map);
|
|
||||||
line.bindPopup(`
|
|
||||||
<strong>${jalan.nama_jalan}</strong><br>
|
|
||||||
Status: ${jalan.status_jalan}<br>
|
|
||||||
Panjang: ${jalan.panjang_meter} meter
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
let currentPoints = [];
|
let currentPoints = [];
|
||||||
let currentLine = null;
|
let currentLine = null;
|
||||||
|
|
||||||
@@ -401,6 +334,7 @@ try {
|
|||||||
dashArray: '8, 8',
|
dashArray: '8, 8',
|
||||||
opacity: 0.85
|
opacity: 0.85
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
currentLine._userDrawn = true;
|
||||||
} else if (currentPoints.length === 1) {
|
} else if (currentPoints.length === 1) {
|
||||||
currentLine = L.circleMarker(currentPoints[0], {
|
currentLine = L.circleMarker(currentPoints[0], {
|
||||||
radius: 6,
|
radius: 6,
|
||||||
@@ -409,6 +343,7 @@ try {
|
|||||||
weight: 2,
|
weight: 2,
|
||||||
fillOpacity: 1
|
fillOpacity: 1
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
currentLine._userDrawn = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,6 +363,43 @@ try {
|
|||||||
|
|
||||||
updateFormFields();
|
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 () {
|
window.addEventListener('load', function () {
|
||||||
map.invalidateSize();
|
map.invalidateSize();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,34 +1,3 @@
|
|||||||
<?php
|
|
||||||
// TAMPILKAN ERROR JIKA ADA YANG SALAH
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
ini_set('display_startup_errors', 1);
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
|
|
||||||
// 1. KONEKSI KE DATABASE MySQL
|
|
||||||
$host = "localhost";
|
|
||||||
$username = "root";
|
|
||||||
$password = "";
|
|
||||||
$database = "webgis_spbu";
|
|
||||||
|
|
||||||
$conn = mysqli_connect($host, $username, $password, $database);
|
|
||||||
|
|
||||||
if (!$conn) {
|
|
||||||
die("Koneksi ke database gagal: " . mysqli_connect_error());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. QUERY AMBIL DATA SPBU
|
|
||||||
$query = mysqli_query($conn, "SELECT * FROM t_spbu");
|
|
||||||
|
|
||||||
if (!$query) {
|
|
||||||
die("Query Error: " . mysqli_error($conn));
|
|
||||||
}
|
|
||||||
|
|
||||||
$data_spbu = [];
|
|
||||||
while ($row = mysqli_fetch_assoc($query)) {
|
|
||||||
$data_spbu[] = $row;
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="id">
|
<html lang="id">
|
||||||
<head>
|
<head>
|
||||||
@@ -65,10 +34,12 @@ while ($row = mysqli_fetch_assoc($query)) {
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<!-- SPBU PAGE FIXED: USE API FETCH FROM api/get_points.php -->
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div id="map"></div>
|
<div id="map"></div>
|
||||||
|
<div id="spbu-status" style="position: fixed; bottom: 16px; right: 16px; z-index: 9999; background: rgba(255,255,255,0.9); color: #111; padding: 8px 12px; border-radius: 12px; box-shadow: 0 8px 30px rgba(0,0,0,0.1); font-size: 13px; font-weight: 600;">SPBU mode: API fetch</div>
|
||||||
|
|
||||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||||
|
|
||||||
@@ -85,36 +56,59 @@ while ($row = mysqli_fetch_assoc($query)) {
|
|||||||
var layerSpbu24Jam = L.layerGroup().addTo(map);
|
var layerSpbu24Jam = L.layerGroup().addTo(map);
|
||||||
var layerSpbuReguler = L.layerGroup().addTo(map);
|
var layerSpbuReguler = L.layerGroup().addTo(map);
|
||||||
|
|
||||||
// Konversi data array PHP ke JSON JavaScript
|
function renderSpbuMarkers(markers) {
|
||||||
var dataSpbuDariDatabase = <?php echo json_encode($data_spbu); ?>;
|
markers.forEach(function(spbu) {
|
||||||
|
var lat = parseFloat(spbu.latitude);
|
||||||
|
var lng = parseFloat(spbu.longitude);
|
||||||
|
if (isNaN(lat) || isNaN(lng)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var isOpen24 = spbu.open_24_hours == 1 || spbu.open_24_hours === '1' || spbu.open_24_hours === true;
|
||||||
|
var warnaMarker = isOpen24 ? '#007BFF' : '#DC3545';
|
||||||
|
|
||||||
// 5. LOOPING DATA & PILAH BERDASARKAN STATUS SPBU
|
var marker = L.circleMarker([lat, lng], {
|
||||||
dataSpbuDariDatabase.forEach(function(spbu) {
|
radius: 8,
|
||||||
var lat = parseFloat(spbu.latitude);
|
fillColor: warnaMarker,
|
||||||
var lng = parseFloat(spbu.longitude);
|
color: '#fff',
|
||||||
|
weight: 2,
|
||||||
|
fillOpacity: 0.9
|
||||||
|
});
|
||||||
|
|
||||||
var warnaMarker = (spbu.status === '24_jam') ? '#007BFF' : '#DC3545';
|
marker.bindPopup(
|
||||||
|
"<strong>" + (spbu.name || spbu.nama_spbu || 'SPBU') + "</strong><br>" +
|
||||||
|
"Status Pelayanan: " + (isOpen24 ? 'Buka 24 Jam' : 'Reguler Biasa') + "<br>" +
|
||||||
|
"Koordinat: " + lat + ", " + lng
|
||||||
|
);
|
||||||
|
|
||||||
var marker = L.circleMarker([lat, lng], {
|
if (isOpen24) {
|
||||||
radius: 8,
|
marker.addTo(layerSpbu24Jam);
|
||||||
fillColor: warnaMarker,
|
} else {
|
||||||
color: '#fff',
|
marker.addTo(layerSpbuReguler);
|
||||||
weight: 2,
|
}
|
||||||
fillOpacity: 0.9
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
marker.bindPopup(
|
function loadSpbuData() {
|
||||||
"<strong>" + spbu.nama_spbu + "</strong><br>" +
|
fetch('api/get_points.php')
|
||||||
"Status Pelayanan: " + (spbu.status === '24_jam' ? 'Buka 24 Jam' : 'Reguler Biasa') + "<br>" +
|
.then(function(response) {
|
||||||
"Koordinat: " + lat + ", " + lng
|
if (!response.ok) {
|
||||||
);
|
throw new Error('HTTP error ' + response.status);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function(data) {
|
||||||
|
if (data.success && Array.isArray(data.markers)) {
|
||||||
|
renderSpbuMarkers(data.markers);
|
||||||
|
} else {
|
||||||
|
console.error('Response tidak berformat expected:', data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
console.error('Gagal memuat data SPBU dari API:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (spbu.status === '24_jam') {
|
loadSpbuData();
|
||||||
marker.addTo(layerSpbu24Jam);
|
|
||||||
} else {
|
|
||||||
marker.addTo(layerSpbuReguler); // <-- Sudah diperbaiki dari layerSpulerReguler
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 6. IMPLEMENTASI LAYERS CONTROL
|
// 6. IMPLEMENTASI LAYERS CONTROL
|
||||||
var overlayMaps = {
|
var overlayMaps = {
|
||||||
|
|||||||
@@ -1,71 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Koneksi database PDO
|
// Data diambil dari API - tidak ada koneksi MySQL di frontend ini
|
||||||
$host = '127.0.0.1';
|
|
||||||
$db = 'webgis_spbu';
|
|
||||||
$user = 'root';
|
|
||||||
$pass = '';
|
|
||||||
$charset = 'utf8mb4';
|
|
||||||
|
|
||||||
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
|
|
||||||
$options = [
|
|
||||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
||||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
||||||
PDO::ATTR_EMULATE_PREPARES => false,
|
|
||||||
];
|
|
||||||
|
|
||||||
try {
|
|
||||||
$pdo = new PDO($dsn, $user, $pass, $options);
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
die('Koneksi gagal: ' . htmlspecialchars($e->getMessage()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buat tabel jika belum ada
|
|
||||||
$createTableSql = "CREATE TABLE IF NOT EXISTS t_tanah (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
nama_pemilik VARCHAR(255) NOT NULL,
|
|
||||||
status_kepemilikan ENUM('SHM', 'HGB', 'HGU', 'HP') NOT NULL,
|
|
||||||
luas_sqm DECIMAL(10,2) NOT NULL,
|
|
||||||
koordinat_json LONGTEXT NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci";
|
|
||||||
$pdo->exec($createTableSql);
|
|
||||||
|
|
||||||
// Simpan data jika form disubmit
|
|
||||||
$successMessage = '';
|
|
||||||
$errorMessage = '';
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
$namaPemilik = trim($_POST['nama_pemilik'] ?? '');
|
|
||||||
$statusKepemilikan = $_POST['status_kepemilikan'] ?? '';
|
|
||||||
$luasSqm = $_POST['luas_sqm'] ?? '';
|
|
||||||
$koordinatJson = $_POST['koordinat_json'] ?? '';
|
|
||||||
|
|
||||||
if ($namaPemilik === '' || $statusKepemilikan === '' || $luasSqm === '' || $koordinatJson === '') {
|
|
||||||
$errorMessage = 'Semua field harus diisi dan polygon harus dibuat sebelum submit.';
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
$stmt = $pdo->prepare('INSERT INTO t_tanah (nama_pemilik, status_kepemilikan, luas_sqm, koordinat_json) VALUES (:nama, :status, :luas, :koordinat)');
|
|
||||||
$stmt->execute([
|
|
||||||
':nama' => $namaPemilik,
|
|
||||||
':status' => $statusKepemilikan,
|
|
||||||
':luas' => $luasSqm,
|
|
||||||
':koordinat' => $koordinatJson,
|
|
||||||
]);
|
|
||||||
$successMessage = 'Data tanah berhasil disimpan.';
|
|
||||||
header('Location: ' . $_SERVER['PHP_SELF'] . '?saved=1');
|
|
||||||
exit;
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
$errorMessage = 'Gagal menyimpan data: ' . htmlspecialchars($e->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ambil semua data polygon
|
|
||||||
$tanahData = [];
|
|
||||||
$stmt = $pdo->query('SELECT id, nama_pemilik, status_kepemilikan, luas_sqm, koordinat_json, created_at FROM t_tanah ORDER BY created_at DESC');
|
|
||||||
while ($row = $stmt->fetch()) {
|
|
||||||
$row['coords'] = json_decode($row['koordinat_json'], true);
|
|
||||||
$tanahData[] = $row;
|
|
||||||
}
|
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="id">
|
<html lang="id">
|
||||||
@@ -119,10 +53,7 @@ while ($row = $stmt->fetch()) {
|
|||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<?php if (!empty($_GET['saved']) && $_GET['saved'] == 1): ?>
|
<?php if (!empty($_GET['saved']) && $_GET['saved'] == 1): ?>
|
||||||
<div class="message success">Data tanah berhasil disimpan.</div>
|
<div class="message success">Data tanah berhasil disimpan dari API.</div>
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($errorMessage): ?>
|
|
||||||
<div class="message error"><?= htmlspecialchars($errorMessage) ?></div>
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<div id="map"></div>
|
<div id="map"></div>
|
||||||
<p class="note">Klik beberapa titik pada peta untuk membuat polygon. Tutup polygon dengan klik titik awal lagi untuk menghitung luas otomatis.</p>
|
<p class="note">Klik beberapa titik pada peta untuk membuat polygon. Tutup polygon dengan klik titik awal lagi untuk menghitung luas otomatis.</p>
|
||||||
@@ -164,19 +95,8 @@ while ($row = $stmt->fetch()) {
|
|||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2>Daftar Parsil Tertinggi & Detail</h2>
|
<h2>Daftar Parsil Tertinggi & Detail</h2>
|
||||||
<div class="info-list">
|
<div class="info-list" id="tanah-list">
|
||||||
<?php if (empty($tanahData)): ?>
|
<div class="info-card">Memuat data dari API...</div>
|
||||||
<div class="info-card">Belum ada data parsil. Tambahkan bidang tanah melalui peta dan form di atas.</div>
|
|
||||||
<?php else: ?>
|
|
||||||
<?php foreach ($tanahData as $tanah): ?>
|
|
||||||
<div class="info-card">
|
|
||||||
<strong><?= htmlspecialchars($tanah['nama_pemilik']) ?> · <?= htmlspecialchars($tanah['status_kepemilikan']) ?></strong>
|
|
||||||
<div>Luas: <?= number_format($tanah['luas_sqm'], 2, ',', '.') ?> m²</div>
|
|
||||||
<div>Disimpan: <?= htmlspecialchars($tanah['created_at']) ?></div>
|
|
||||||
<div style="margin-top: 10px;"><span class="status-pill status-<?= strtolower($tanah['status_kepemilikan']) ?>"><?= htmlspecialchars($tanah['status_kepemilikan']) ?></span></div>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -201,20 +121,59 @@ while ($row = $stmt->fetch()) {
|
|||||||
HP: '#1d4ed8'
|
HP: '#1d4ed8'
|
||||||
};
|
};
|
||||||
|
|
||||||
const existingData = <?= json_encode($tanahData, JSON_UNESCAPED_UNICODE) ?>;
|
let existingData = [];
|
||||||
existingData.forEach(item => {
|
|
||||||
if (!Array.isArray(item.coords)) return;
|
// Load data from API
|
||||||
const coords = item.coords.map(c => [c.lat, c.lng]);
|
function loadTanahDataFromAPI() {
|
||||||
const fillColor = statusColor[item.status_kepemilikan] || '#475569';
|
fetch('api/get_parcels.php')
|
||||||
const poly = L.polygon(coords, {
|
.then(response => response.json())
|
||||||
color: fillColor,
|
.then(data => {
|
||||||
fillColor: fillColor,
|
if (data.success && Array.isArray(data.parcels)) {
|
||||||
fillOpacity: 0.15,
|
existingData = data.parcels;
|
||||||
weight: 2,
|
renderExistingParcels();
|
||||||
opacity: 0.9
|
}
|
||||||
}).addTo(polygonLayerGroup);
|
})
|
||||||
poly.bindPopup(`<strong>${escapeHtml(item.nama_pemilik)}</strong><br>${escapeHtml(item.status_kepemilikan)}<br>Luas: ${Number(item.luas_sqm).toLocaleString('id-ID', {minimumFractionDigits:2, maximumFractionDigits:2})} m²`);
|
.catch(error => console.error('Error loading parcels:', error));
|
||||||
});
|
}
|
||||||
|
|
||||||
|
function renderExistingParcels() {
|
||||||
|
// Clear existing layers
|
||||||
|
polygonLayerGroup.clearLayers();
|
||||||
|
|
||||||
|
// Render parcels on map
|
||||||
|
existingData.forEach(item => {
|
||||||
|
const coords = typeof item.coordinates === 'string' ? JSON.parse(item.coordinates) : item.coordinates;
|
||||||
|
if (!Array.isArray(coords)) return;
|
||||||
|
|
||||||
|
const fillColor = statusColor[item.owner_status] || '#475569';
|
||||||
|
const poly = L.polygon(coords.map(c => [c.lat || c[0], c.lng || c[1]]), {
|
||||||
|
color: fillColor,
|
||||||
|
fillColor: fillColor,
|
||||||
|
fillOpacity: 0.15,
|
||||||
|
weight: 2,
|
||||||
|
opacity: 0.9
|
||||||
|
}).addTo(polygonLayerGroup);
|
||||||
|
|
||||||
|
poly.bindPopup(`<strong>${escapeHtml(item.owner_name || 'N/A')}</strong><br>${escapeHtml(item.owner_status)}<br>Luas: ${Number(item.area).toLocaleString('id-ID', {minimumFractionDigits:2, maximumFractionDigits:2})} m²`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update list display
|
||||||
|
const listContainer = document.getElementById('tanah-list');
|
||||||
|
if (existingData.length === 0) {
|
||||||
|
listContainer.innerHTML = '<div class="info-card">Belum ada data parsil yang tersimpan.</div>';
|
||||||
|
} else {
|
||||||
|
listContainer.innerHTML = existingData.map(item => `
|
||||||
|
<div class="info-card">
|
||||||
|
<strong>${escapeHtml(item.owner_name || 'N/A')} · ${escapeHtml(item.owner_status)}</strong>
|
||||||
|
<div>Luas: ${Number(item.area).toLocaleString('id-ID', {minimumFractionDigits:2, maximumFractionDigits:2})} m²</div>
|
||||||
|
<div style="margin-top: 10px;"><span class="status-pill status-${item.owner_status.toLowerCase()}">${escapeHtml(item.owner_status)}</span></div>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load data on page load
|
||||||
|
loadTanahDataFromAPI();
|
||||||
|
|
||||||
function escapeHtml(str) {
|
function escapeHtml(str) {
|
||||||
return String(str)
|
return String(str)
|
||||||
@@ -337,10 +296,39 @@ while ($row = $stmt->fetch()) {
|
|||||||
|
|
||||||
const form = document.getElementById('tanahForm');
|
const form = document.getElementById('tanahForm');
|
||||||
form.addEventListener('submit', event => {
|
form.addEventListener('submit', event => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
if (currentCoordinates.length < 4 || !document.getElementById('luas_sqm').value) {
|
if (currentCoordinates.length < 4 || !document.getElementById('luas_sqm').value) {
|
||||||
event.preventDefault();
|
|
||||||
alert('Buat polygon tertutup terlebih dahulu sebelum submit. Klik titik awal lagi untuk menutup polygon.');
|
alert('Buat polygon tertutup terlebih dahulu sebelum submit. Klik titik awal lagi untuk menutup polygon.');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formData = {
|
||||||
|
owner_name: document.getElementById('nama_pemilik').value,
|
||||||
|
owner_status: document.getElementById('status_kepemilikan').value,
|
||||||
|
area: document.getElementById('luas_sqm').value,
|
||||||
|
coordinates: document.getElementById('koordinat_json').value
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch('api/save_parcel.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 parsil berhasil disimpan!');
|
||||||
|
currentCoordinates = [];
|
||||||
|
redrawCurrentShape();
|
||||||
|
updateForm();
|
||||||
|
form.reset();
|
||||||
|
loadTanahDataFromAPI();
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + (data.message || 'Gagal menyimpan data'));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => alert('Error: ' + error));
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user