From c2c0774b3b66e05d869a457544e01de1136c6e3e Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 9 Jun 2026 19:02:22 +0700 Subject: [PATCH] First commit --- Manajemen_Jalan_dan_Parsil/WEB_GIS.sql | 16 + Manajemen_Jalan_dan_Parsil/WebGIS.db | Bin 0 -> 8192 bytes Manajemen_Jalan_dan_Parsil/api/db.php | 35 + Manajemen_Jalan_dan_Parsil/api/jalan.php | 247 ++ Manajemen_Jalan_dan_Parsil/api/parsil.php | 246 ++ Manajemen_Jalan_dan_Parsil/extract_css.py | 19 + Manajemen_Jalan_dan_Parsil/index.html | 974 +++++++ Manajemen_Jalan_dan_Parsil/index_backup.html | 1273 +++++++++ .../sql/webgis_struktur.sql | 67 + Manajemen_Jalan_dan_Parsil/style.css | 822 ++++++ WebGIS_PovertyMap/api_auth.php | 82 + WebGIS_PovertyMap/api_bantuan.php | 129 + WebGIS_PovertyMap/api_dashboard.php | 141 + WebGIS_PovertyMap/api_jalan.php | 165 ++ WebGIS_PovertyMap/api_kemiskinan.php | 192 ++ WebGIS_PovertyMap/api_lapor.php | 218 ++ WebGIS_PovertyMap/api_masjid.php | 182 ++ WebGIS_PovertyMap/api_parsil.php | 169 ++ WebGIS_PovertyMap/backup_webgis_p3.sql | 0 WebGIS_PovertyMap/config.php | 33 + WebGIS_PovertyMap/dashboard_walikota.html | 474 ++++ WebGIS_PovertyMap/database.sql | 88 + WebGIS_PovertyMap/database_lengkap.sql | 132 + WebGIS_PovertyMap/generate_hash.php | 94 + WebGIS_PovertyMap/index.html | 2398 +++++++++++++++++ WebGIS_PovertyMap/login.html | 846 ++++++ WebGIS_PovertyMap/migrasi_proses_bisnis.sql | 101 + WebGIS_PovertyMap/migrate_lapor.php | 28 + WebGIS_PovertyMap/migrate_lapor2.php | 29 + WebGIS_PovertyMap/test_db.php | 11 + WebGIS_PovertyMap/users_migration.sql | 89 + WebGIS_SPBU/api_spbu.php | 197 ++ WebGIS_SPBU/config.php | 103 + WebGIS_SPBU/database.sql | 38 + WebGIS_SPBU/index.html | 562 ++++ WebGIS_SPBU/style.css | 293 ++ 36 files changed, 10493 insertions(+) create mode 100644 Manajemen_Jalan_dan_Parsil/WEB_GIS.sql create mode 100644 Manajemen_Jalan_dan_Parsil/WebGIS.db create mode 100644 Manajemen_Jalan_dan_Parsil/api/db.php create mode 100644 Manajemen_Jalan_dan_Parsil/api/jalan.php create mode 100644 Manajemen_Jalan_dan_Parsil/api/parsil.php create mode 100644 Manajemen_Jalan_dan_Parsil/extract_css.py create mode 100644 Manajemen_Jalan_dan_Parsil/index.html create mode 100644 Manajemen_Jalan_dan_Parsil/index_backup.html create mode 100644 Manajemen_Jalan_dan_Parsil/sql/webgis_struktur.sql create mode 100644 Manajemen_Jalan_dan_Parsil/style.css create mode 100644 WebGIS_PovertyMap/api_auth.php create mode 100644 WebGIS_PovertyMap/api_bantuan.php create mode 100644 WebGIS_PovertyMap/api_dashboard.php create mode 100644 WebGIS_PovertyMap/api_jalan.php create mode 100644 WebGIS_PovertyMap/api_kemiskinan.php create mode 100644 WebGIS_PovertyMap/api_lapor.php create mode 100644 WebGIS_PovertyMap/api_masjid.php create mode 100644 WebGIS_PovertyMap/api_parsil.php create mode 100644 WebGIS_PovertyMap/backup_webgis_p3.sql create mode 100644 WebGIS_PovertyMap/config.php create mode 100644 WebGIS_PovertyMap/dashboard_walikota.html create mode 100644 WebGIS_PovertyMap/database.sql create mode 100644 WebGIS_PovertyMap/database_lengkap.sql create mode 100644 WebGIS_PovertyMap/generate_hash.php create mode 100644 WebGIS_PovertyMap/index.html create mode 100644 WebGIS_PovertyMap/login.html create mode 100644 WebGIS_PovertyMap/migrasi_proses_bisnis.sql create mode 100644 WebGIS_PovertyMap/migrate_lapor.php create mode 100644 WebGIS_PovertyMap/migrate_lapor2.php create mode 100644 WebGIS_PovertyMap/test_db.php create mode 100644 WebGIS_PovertyMap/users_migration.sql create mode 100644 WebGIS_SPBU/api_spbu.php create mode 100644 WebGIS_SPBU/config.php create mode 100644 WebGIS_SPBU/database.sql create mode 100644 WebGIS_SPBU/index.html create mode 100644 WebGIS_SPBU/style.css diff --git a/Manajemen_Jalan_dan_Parsil/WEB_GIS.sql b/Manajemen_Jalan_dan_Parsil/WEB_GIS.sql new file mode 100644 index 0000000..8df31fd --- /dev/null +++ b/Manajemen_Jalan_dan_Parsil/WEB_GIS.sql @@ -0,0 +1,16 @@ +-- 1. Membuat database baru (misalnya kita beri nama 'gis_spbu') +CREATE DATABASE WEB_GIS; + +-- 2. Memilih database tersebut untuk digunakan +USE WEB_GIS; + +-- 3. Membuat tabel di dalam database yang sudah dipilih +CREATE TABLE Poin_SPBU ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama_spbu VARCHAR(150) NOT NULL, + no_wa VARCHAR(20) NOT NULL, + buka_24_jam ENUM('Ya', 'Tidak') NOT NULL, + latitude DECIMAL(10, 8) NOT NULL, + longitude DECIMAL(11, 8) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/Manajemen_Jalan_dan_Parsil/WebGIS.db b/Manajemen_Jalan_dan_Parsil/WebGIS.db new file mode 100644 index 0000000000000000000000000000000000000000..9439ba0f2f35944d120cc89829f5e6caca8f6dd0 GIT binary patch literal 8192 zcmeIuJqp4=5QgE|Aa(ggE)a2*Me}3WCZ5X!YQP1%^zsf2-!e07 z@f`Q5^HwiaQ{-NkG7)2Br79xPPn++1@X1i!tgj+jJRhU(Y9caz^_{av009ILKmY** z5I_I{1Q0*~fgrFm!)Y8F=kwFumX)_f?c0#i_K>WzM6>lKO*Bk(=FaL$Z?Cq|x^YF` gw0g7cJp4%_fB*srAbl>h($ literal 0 HcmV?d00001 diff --git a/Manajemen_Jalan_dan_Parsil/api/db.php b/Manajemen_Jalan_dan_Parsil/api/db.php new file mode 100644 index 0000000..83ee258 --- /dev/null +++ b/Manajemen_Jalan_dan_Parsil/api/db.php @@ -0,0 +1,35 @@ +connect_error) { + http_response_code(500); + echo json_encode([ + 'status' => 'gagal', + 'pesan' => 'Koneksi database gagal: ' . $conn->connect_error + ]); + exit; + } + + $conn->set_charset('utf8mb4'); + return $conn; +} diff --git a/Manajemen_Jalan_dan_Parsil/api/jalan.php b/Manajemen_Jalan_dan_Parsil/api/jalan.php new file mode 100644 index 0000000..dee7051 --- /dev/null +++ b/Manajemen_Jalan_dan_Parsil/api/jalan.php @@ -0,0 +1,247 @@ +prepare( + "SELECT id, nama_jalan, status_jalan, panjang_jalan, geojson, created_at + FROM tb_jalan WHERE id = ?" + ); + $stmt->bind_param('i', $id); + $stmt->execute(); + $result = $stmt->get_result(); + $row = $result->fetch_assoc(); + + if ($row) { + $row['geojson'] = json_decode($row['geojson']); // parse ke objek + echo json_encode($row); + } else { + http_response_code(404); + echo json_encode(['status' => 'gagal', 'pesan' => 'Data jalan tidak ditemukan']); + } + + } else { + /* Semua data */ + $filter = isset($_GET['status']) ? $_GET['status'] : null; + + if ($filter) { + $stmt = $conn->prepare( + "SELECT id, nama_jalan, status_jalan, panjang_jalan, geojson, created_at + FROM tb_jalan WHERE status_jalan = ? ORDER BY created_at DESC" + ); + $stmt->bind_param('s', $filter); + $stmt->execute(); + $result = $stmt->get_result(); + } else { + $result = $conn->query( + "SELECT id, nama_jalan, status_jalan, panjang_jalan, geojson, created_at + FROM tb_jalan ORDER BY created_at DESC" + ); + } + + $data = []; + while ($row = $result->fetch_assoc()) { + $row['geojson'] = json_decode($row['geojson']); + $data[] = $row; + } + echo json_encode($data); + } + break; + + /* ============================================================ + CREATE — Simpan data jalan baru + Body (JSON): + { + "nama_jalan" : "Jalan Ahmad Yani", + "status_jalan" : "Nasional", // Nasional|Provinsi|Kabupaten + "panjang_jalan" : 2345.67, // meter, dihitung oleh LeafletJS + "geojson" : { // GeoJSON LineString + "type": "LineString", + "coordinates": [[lng,lat], ...] + } + } + ============================================================ */ + case 'POST': + $body = json_decode(file_get_contents('php://input'), true); + + if (!$body) { + http_response_code(400); + echo json_encode(['status' => 'gagal', 'pesan' => 'Body request tidak valid atau bukan JSON']); + break; + } + + $nama_jalan = trim($body['nama_jalan'] ?? ''); + $status_jalan = trim($body['status_jalan'] ?? ''); + $panjang_jalan = (float)($body['panjang_jalan'] ?? 0); + $geojson_raw = $body['geojson'] ?? null; + + /* Validasi */ + $valid_status = ['Nasional', 'Provinsi', 'Kabupaten']; + if (!$nama_jalan) { + http_response_code(400); + echo json_encode(['status' => 'gagal', 'pesan' => 'nama_jalan tidak boleh kosong']); + break; + } + if (!in_array($status_jalan, $valid_status)) { + http_response_code(400); + echo json_encode(['status' => 'gagal', 'pesan' => 'status_jalan tidak valid. Pilih: Nasional, Provinsi, atau Kabupaten']); + break; + } + if (!$geojson_raw || ($geojson_raw['type'] ?? '') !== 'LineString') { + http_response_code(400); + echo json_encode(['status' => 'gagal', 'pesan' => 'geojson harus bertipe LineString']); + break; + } + + $geojson_str = json_encode($geojson_raw); + + $stmt = $conn->prepare( + "INSERT INTO tb_jalan (nama_jalan, status_jalan, panjang_jalan, geojson) + VALUES (?, ?, ?, ?)" + ); + $stmt->bind_param('ssds', $nama_jalan, $status_jalan, $panjang_jalan, $geojson_str); + + if ($stmt->execute()) { + echo json_encode([ + 'status' => 'sukses', + 'id' => $conn->insert_id, + 'pesan' => 'Data jalan berhasil disimpan' + ]); + } else { + throw new Exception($stmt->error); + } + break; + + /* ============================================================ + UPDATE — Perbarui atribut dan/atau koordinat jalan + Mengirim field yang ingin diubah saja: + - Edit atribut : { "nama_jalan": "...", "status_jalan": "..." } + - Geser vertex : { "geojson": {...}, "panjang_jalan": 1234.5 } + - Keduanya : { semua field } + ============================================================ */ + case 'PUT': + if (!$id) { + http_response_code(400); + echo json_encode(['status' => 'gagal', 'pesan' => 'Parameter id diperlukan']); + break; + } + + $body = json_decode(file_get_contents('php://input'), true); + if (!$body) { + http_response_code(400); + echo json_encode(['status' => 'gagal', 'pesan' => 'Body tidak valid']); + break; + } + + /* Bangun query dinamis berdasarkan field yang dikirim */ + $setClauses = []; + $paramVals = []; + $paramTypes = ''; + + if (array_key_exists('nama_jalan', $body)) { + $v = trim($body['nama_jalan']); + if (!$v) { http_response_code(400); echo json_encode(['status'=>'gagal','pesan'=>'nama_jalan tidak boleh kosong']); break; } + $setClauses[] = 'nama_jalan = ?'; $paramVals[] = $v; $paramTypes .= 's'; + } + if (array_key_exists('status_jalan', $body)) { + $valid = ['Nasional','Provinsi','Kabupaten']; + if (!in_array($body['status_jalan'], $valid)) { + http_response_code(400); echo json_encode(['status'=>'gagal','pesan'=>'status_jalan tidak valid']); break; + } + $setClauses[] = 'status_jalan = ?'; $paramVals[] = $body['status_jalan']; $paramTypes .= 's'; + } + if (array_key_exists('panjang_jalan', $body)) { + $setClauses[] = 'panjang_jalan = ?'; $paramVals[] = (float)$body['panjang_jalan']; $paramTypes .= 'd'; + } + if (array_key_exists('geojson', $body)) { + $setClauses[] = 'geojson = ?'; $paramVals[] = json_encode($body['geojson']); $paramTypes .= 's'; + } + + if (empty($setClauses)) { + echo json_encode(['status' => 'gagal', 'pesan' => 'Tidak ada field yang dikirim untuk diupdate']); + break; + } + + $paramVals[] = $id; + $paramTypes .= 'i'; + + $sql = 'UPDATE tb_jalan SET ' . implode(', ', $setClauses) . ' WHERE id = ?'; + $stmt = $conn->prepare($sql); + $stmt->bind_param($paramTypes, ...$paramVals); + + if ($stmt->execute()) { + echo json_encode(['status' => 'sukses', 'pesan' => 'Data jalan berhasil diperbarui']); + } else { + throw new Exception($stmt->error); + } + break; + + /* ============================================================ + DELETE — Hapus data jalan + ============================================================ */ + case 'DELETE': + if (!$id) { + http_response_code(400); + echo json_encode(['status' => 'gagal', 'pesan' => 'Parameter id diperlukan']); + break; + } + + $stmt = $conn->prepare("DELETE FROM tb_jalan WHERE id = ?"); + $stmt->bind_param('i', $id); + + if ($stmt->execute() && $stmt->affected_rows > 0) { + echo json_encode(['status' => 'sukses', 'pesan' => 'Data jalan berhasil dihapus']); + } else { + http_response_code(404); + echo json_encode(['status' => 'gagal', 'pesan' => 'Data tidak ditemukan atau sudah dihapus']); + } + break; + + default: + http_response_code(405); + echo json_encode(['status' => 'gagal', 'pesan' => 'Method tidak didukung']); + } + +} catch (Exception $e) { + http_response_code(500); + echo json_encode(['status' => 'gagal', 'pesan' => 'Server error: ' . $e->getMessage()]); +} finally { + if ($conn) $conn->close(); +} diff --git a/Manajemen_Jalan_dan_Parsil/api/parsil.php b/Manajemen_Jalan_dan_Parsil/api/parsil.php new file mode 100644 index 0000000..bf07f73 --- /dev/null +++ b/Manajemen_Jalan_dan_Parsil/api/parsil.php @@ -0,0 +1,246 @@ +prepare( + "SELECT id, nama_parsil, status_kepemilikan, luas_tanah, geojson, created_at + FROM tb_parsil WHERE id = ?" + ); + $stmt->bind_param('i', $id); + $stmt->execute(); + $result = $stmt->get_result(); + $row = $result->fetch_assoc(); + + if ($row) { + $row['geojson'] = json_decode($row['geojson']); + echo json_encode($row); + } else { + http_response_code(404); + echo json_encode(['status' => 'gagal', 'pesan' => 'Data parsil tidak ditemukan']); + } + + } else { + /* Semua data */ + $filter = isset($_GET['status']) ? $_GET['status'] : null; + + if ($filter) { + $stmt = $conn->prepare( + "SELECT id, nama_parsil, status_kepemilikan, luas_tanah, geojson, created_at + FROM tb_parsil WHERE status_kepemilikan = ? ORDER BY created_at DESC" + ); + $stmt->bind_param('s', $filter); + $stmt->execute(); + $result = $stmt->get_result(); + } else { + $result = $conn->query( + "SELECT id, nama_parsil, status_kepemilikan, luas_tanah, geojson, created_at + FROM tb_parsil ORDER BY created_at DESC" + ); + } + + $data = []; + while ($row = $result->fetch_assoc()) { + $row['geojson'] = json_decode($row['geojson']); + $data[] = $row; + } + echo json_encode($data); + } + break; + + /* ============================================================ + CREATE — Simpan data parsil baru + Body (JSON): + { + "nama_parsil" : "Kavling A-01", + "status_kepemilikan" : "SHM", // SHM|HGB|HGU|HP + "luas_tanah" : 1250.5000, // m², dihitung LeafletJS + "geojson" : { // GeoJSON Polygon + "type": "Polygon", + "coordinates": [[[lng,lat], ...]] + } + } + ============================================================ */ + case 'POST': + $body = json_decode(file_get_contents('php://input'), true); + + if (!$body) { + http_response_code(400); + echo json_encode(['status' => 'gagal', 'pesan' => 'Body request tidak valid atau bukan JSON']); + break; + } + + $nama_parsil = trim($body['nama_parsil'] ?? ''); + $status_kepemilikan = trim($body['status_kepemilikan'] ?? ''); + $luas_tanah = (float)($body['luas_tanah'] ?? 0); + $geojson_raw = $body['geojson'] ?? null; + + /* Validasi */ + $valid_status = ['SHM', 'HGB', 'HGU', 'HP']; + if (!$nama_parsil) { + http_response_code(400); + echo json_encode(['status' => 'gagal', 'pesan' => 'nama_parsil tidak boleh kosong']); + break; + } + if (!in_array($status_kepemilikan, $valid_status)) { + http_response_code(400); + echo json_encode(['status' => 'gagal', 'pesan' => 'status_kepemilikan tidak valid. Pilih: SHM, HGB, HGU, atau HP']); + break; + } + if (!$geojson_raw || ($geojson_raw['type'] ?? '') !== 'Polygon') { + http_response_code(400); + echo json_encode(['status' => 'gagal', 'pesan' => 'geojson harus bertipe Polygon']); + break; + } + + $geojson_str = json_encode($geojson_raw); + + $stmt = $conn->prepare( + "INSERT INTO tb_parsil (nama_parsil, status_kepemilikan, luas_tanah, geojson) + VALUES (?, ?, ?, ?)" + ); + $stmt->bind_param('ssds', $nama_parsil, $status_kepemilikan, $luas_tanah, $geojson_str); + + if ($stmt->execute()) { + echo json_encode([ + 'status' => 'sukses', + 'id' => $conn->insert_id, + 'pesan' => 'Data parsil berhasil disimpan' + ]); + } else { + throw new Exception($stmt->error); + } + break; + + /* ============================================================ + UPDATE — Perbarui atribut dan/atau koordinat parsil + Mengirim field yang ingin diubah saja: + - Edit atribut : { "nama_parsil": "...", "status_kepemilikan": "..." } + - Geser vertex : { "geojson": {...}, "luas_tanah": 1234.5 } + - Keduanya : { semua field } + ============================================================ */ + case 'PUT': + if (!$id) { + http_response_code(400); + echo json_encode(['status' => 'gagal', 'pesan' => 'Parameter id diperlukan']); + break; + } + + $body = json_decode(file_get_contents('php://input'), true); + if (!$body) { + http_response_code(400); + echo json_encode(['status' => 'gagal', 'pesan' => 'Body tidak valid']); + break; + } + + /* Bangun query dinamis berdasarkan field yang dikirim */ + $setClauses = []; + $paramVals = []; + $paramTypes = ''; + + if (array_key_exists('nama_parsil', $body)) { + $v = trim($body['nama_parsil']); + if (!$v) { http_response_code(400); echo json_encode(['status'=>'gagal','pesan'=>'nama_parsil tidak boleh kosong']); break; } + $setClauses[] = 'nama_parsil = ?'; $paramVals[] = $v; $paramTypes .= 's'; + } + if (array_key_exists('status_kepemilikan', $body)) { + $valid = ['SHM','HGB','HGU','HP']; + if (!in_array($body['status_kepemilikan'], $valid)) { + http_response_code(400); echo json_encode(['status'=>'gagal','pesan'=>'status_kepemilikan tidak valid']); break; + } + $setClauses[] = 'status_kepemilikan = ?'; $paramVals[] = $body['status_kepemilikan']; $paramTypes .= 's'; + } + if (array_key_exists('luas_tanah', $body)) { + $setClauses[] = 'luas_tanah = ?'; $paramVals[] = (float)$body['luas_tanah']; $paramTypes .= 'd'; + } + if (array_key_exists('geojson', $body)) { + $setClauses[] = 'geojson = ?'; $paramVals[] = json_encode($body['geojson']); $paramTypes .= 's'; + } + + if (empty($setClauses)) { + echo json_encode(['status' => 'gagal', 'pesan' => 'Tidak ada field yang dikirim untuk diupdate']); + break; + } + + $paramVals[] = $id; + $paramTypes .= 'i'; + + $sql = 'UPDATE tb_parsil SET ' . implode(', ', $setClauses) . ' WHERE id = ?'; + $stmt = $conn->prepare($sql); + $stmt->bind_param($paramTypes, ...$paramVals); + + if ($stmt->execute()) { + echo json_encode(['status' => 'sukses', 'pesan' => 'Data parsil berhasil diperbarui']); + } else { + throw new Exception($stmt->error); + } + break; + + /* ============================================================ + DELETE — Hapus data parsil + ============================================================ */ + case 'DELETE': + if (!$id) { + http_response_code(400); + echo json_encode(['status' => 'gagal', 'pesan' => 'Parameter id diperlukan']); + break; + } + + $stmt = $conn->prepare("DELETE FROM tb_parsil WHERE id = ?"); + $stmt->bind_param('i', $id); + + if ($stmt->execute() && $stmt->affected_rows > 0) { + echo json_encode(['status' => 'sukses', 'pesan' => 'Data parsil berhasil dihapus']); + } else { + http_response_code(404); + echo json_encode(['status' => 'gagal', 'pesan' => 'Data tidak ditemukan atau sudah dihapus']); + } + break; + + default: + http_response_code(405); + echo json_encode(['status' => 'gagal', 'pesan' => 'Method tidak didukung']); + } + +} catch (Exception $e) { + http_response_code(500); + echo json_encode(['status' => 'gagal', 'pesan' => 'Server error: ' . $e->getMessage()]); +} finally { + if ($conn) $conn->close(); +} diff --git a/Manajemen_Jalan_dan_Parsil/extract_css.py b/Manajemen_Jalan_dan_Parsil/extract_css.py new file mode 100644 index 0000000..e58e276 --- /dev/null +++ b/Manajemen_Jalan_dan_Parsil/extract_css.py @@ -0,0 +1,19 @@ +import re + +# 1. Read 'pertemuan fix/index.html' +with open(r"D:\laragon\www\WebGIS\pertemuan fix\index.html", "r", encoding="utf-8") as f: + ref_content = f.read() + +# Extract CSS +css_match = re.search(r'', ref_content, re.DOTALL) +css_content = css_match.group(1) if css_match else "" + +# Replace specific variables in CSS for the new context +css_content = css_content.replace('--kemiskinan', '--jalan') +css_content = css_content.replace('--masjid', '--parsil') + +# We'll save the CSS as style.css +with open(r"D:\laragon\www\WebGIS\WebGIS\style.css", "w", encoding="utf-8") as f: + f.write(css_content) + +print("Extracted style.css") diff --git a/Manajemen_Jalan_dan_Parsil/index.html b/Manajemen_Jalan_dan_Parsil/index.html new file mode 100644 index 0000000..2f85b50 --- /dev/null +++ b/Manajemen_Jalan_dan_Parsil/index.html @@ -0,0 +1,974 @@ + + + + + + WebGIS — Manajemen Jalan & Parsil Tanah + + + + + + + + + + +
+
+ 🗺️ +

WebGIS Manajemen Jalan & Parsil

+
+
+
+
+ Sistem Online +
+
+
+ +
+ + +
+ + + + + + + + +
+
+
+ 🛣️ Tambah Jalan +
+
+ 🏘️ Tambah Parsil +
+
+ +
+ +
+ + + + + + + + + + + + diff --git a/Manajemen_Jalan_dan_Parsil/index_backup.html b/Manajemen_Jalan_dan_Parsil/index_backup.html new file mode 100644 index 0000000..d453237 --- /dev/null +++ b/Manajemen_Jalan_dan_Parsil/index_backup.html @@ -0,0 +1,1273 @@ + + + + + + WebGIS — Manajemen Jalan & Parsil Tanah + + + + + + + + +
+ + + + + +
+
+ + +
+ + + + + + +
+ + +
+ + +
+
Legenda
+
+
🛣️ Status Jalan
+
Jalan Nasional
+
Jalan Provinsi
+
Jalan Kabupaten
+
+
+
🏘️ Status Kepemilikan
+
SHM
+
HGB
+
HGU
+
HP
+
+
+
+ +
+ + + + + + + + + + diff --git a/Manajemen_Jalan_dan_Parsil/sql/webgis_struktur.sql b/Manajemen_Jalan_dan_Parsil/sql/webgis_struktur.sql new file mode 100644 index 0000000..e9a2448 --- /dev/null +++ b/Manajemen_Jalan_dan_Parsil/sql/webgis_struktur.sql @@ -0,0 +1,67 @@ +-- ========================================================= +-- WebGIS - Struktur Database +-- Tugas: Manajemen Data Jalan & Parsil Tanah (Kavling) +-- Database: WEB_GIS +-- Tipe Geometri: GeoJSON (LineString & Polygon) +-- ========================================================= + +CREATE DATABASE IF NOT EXISTS WEB_GIS + CHARACTER SET utf8mb4 + COLLATE utf8mb4_unicode_ci; + +USE WEB_GIS; + +-- --------------------------------------------------------- +-- TABEL 1: Manajemen Data Jalan +-- Tipe Geometri : GeoJSON LineString +-- Panjang Jalan : Dihitung otomatis oleh LeafletJS (meter) +-- --------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `tb_jalan` ( + `id` INT NOT NULL AUTO_INCREMENT COMMENT 'Primary key jalan', + `nama_jalan` VARCHAR(100) NOT NULL COMMENT 'Nama ruas jalan', + `status_jalan` ENUM( + 'Nasional', + 'Provinsi', + 'Kabupaten' + ) NOT NULL COMMENT 'Status administrasi jalan', + `panjang_jalan` DECIMAL(12,2) NOT NULL DEFAULT '0.00' COMMENT 'Panjang jalan dalam meter (dihitung otomatis LeafletJS via L.LatLng.distanceTo)', + `geojson` JSON NOT NULL COMMENT 'Koordinat geometri dalam format GeoJSON LineString', + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB + DEFAULT CHARSET=utf8mb4 + COLLATE=utf8mb4_unicode_ci + COMMENT='Tabel manajemen data jalan berdasarkan status administrasi. Geometri disimpan sebagai GeoJSON LineString.'; + + +-- --------------------------------------------------------- +-- TABEL 2: Manajemen Parsil Tanah / Kavling +-- Tipe Geometri : GeoJSON Polygon +-- Luas Tanah : Dihitung otomatis oleh LeafletJS (m²) +-- --------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `tb_parsil` ( + `id` INT NOT NULL AUTO_INCREMENT COMMENT 'Primary key parsil', + `nama_parsil` VARCHAR(100) NOT NULL COMMENT 'Nama atau nomor identifikasi parsil tanah', + `status_kepemilikan` ENUM( + 'SHM', + 'HGB', + 'HGU', + 'HP' + ) NOT NULL COMMENT 'Status sertifikat kepemilikan: SHM=Sertifikat Hak Milik, HGB=Hak Guna Bangunan, HGU=Hak Guna Usaha, HP=Hak Pakai', + `luas_tanah` DECIMAL(16,4) NOT NULL DEFAULT '0.0000' COMMENT 'Luas tanah dalam meter persegi (dihitung otomatis LeafletJS via formula geodesic spherical)', + `geojson` JSON NOT NULL COMMENT 'Koordinat geometri dalam format GeoJSON Polygon', + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB + DEFAULT CHARSET=utf8mb4 + COLLATE=utf8mb4_unicode_ci + COMMENT='Tabel manajemen parsil/kavling tanah berdasarkan status kepemilikan. Geometri disimpan sebagai GeoJSON Polygon.'; + + +-- --------------------------------------------------------- +-- Contoh Indeks untuk performa query filter +-- --------------------------------------------------------- +ALTER TABLE `tb_jalan` ADD INDEX `idx_status_jalan` (`status_jalan`); +ALTER TABLE `tb_parsil` ADD INDEX `idx_status_parsil` (`status_kepemilikan`); diff --git a/Manajemen_Jalan_dan_Parsil/style.css b/Manajemen_Jalan_dan_Parsil/style.css new file mode 100644 index 0000000..292cf3d --- /dev/null +++ b/Manajemen_Jalan_dan_Parsil/style.css @@ -0,0 +1,822 @@ + + :root { + --bg-dark: #0f1117; + --bg-sidebar: #161b27; + --bg-card: #1e2538; + --bg-card-hover: #252d42; + --border: #2a3248; + --accent-blue: #3b82f6; + --accent-green: #10b981; + --accent-yellow: #f59e0b; + --accent-red: #ef4444; + --text-primary: #f1f5f9; + --text-secondary: #94a3b8; + --text-muted: #4b5563; + --radius-sm: 6px; + --radius-md: 10px; + --shadow: 0 4px 24px rgba(0, 0, 0, .4); + --jalan: #f97316; + --parsil: #8b5cf6; + } + + * { + margin: 0; + padding: 0; + box-sizing: border-box; + } + + body { + font-family: 'Inter', sans-serif; + background: var(--bg-dark); + color: var(--text-primary); + height: 100vh; + display: flex; + flex-direction: column; + overflow: hidden; + } + + #app-container { + display: flex; + flex: 1; + overflow: hidden; + } + + #header-bar { + background: linear-gradient(135deg, #161b27 0%, #0f1117 100%); + border-bottom: 1px solid var(--border); + padding: 12px 20px; + display: flex; + justify-content: space-between; + align-items: center; + z-index: 1000; + } + + /* ── Auth / User Badge ── */ + .user-badge { + display: flex; + align-items: center; + gap: 10px; + } + + .user-info { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 1px; + } + + .user-name { + font-size: 12px; + font-weight: 700; + color: var(--text-primary); + } + + .role-badge { + font-size: 9px; + font-weight: 700; + padding: 2px 8px; + border-radius: 20px; + letter-spacing: .5px; + text-transform: uppercase; + } + + .role-badge-admin { + background: rgba(59, 130, 246, .2); + color: #60a5fa; + border: 1px solid rgba(59, 130, 246, .4); + } + + .role-badge-pengurus { + background: rgba(139, 92, 246, .2); + color: #a78bfa; + border: 1px solid rgba(139, 92, 246, .4); + } + + .role-badge-walikota { + background: rgba(249, 115, 22, .2); + color: #fb923c; + border: 1px solid rgba(249, 115, 22, .4); + } + + .btn-login { + padding: 6px 16px; + background: linear-gradient(135deg, var(--accent-blue), #2563eb); + border: none; + border-radius: 6px; + color: #fff; + font-size: 12px; + font-weight: 600; + font-family: 'Inter', sans-serif; + cursor: pointer; + transition: all .2s; + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 6px; + } + + .btn-login:hover { + opacity: .9; + transform: translateY(-1px); + } + + .btn-lapor-header { + padding: 6px 14px; + background: rgba(239, 68, 68, .15); + border: 1px solid rgba(239, 68, 68, .35); + border-radius: 6px; + color: #f87171; + font-size: 11px; + font-weight: 600; + font-family: 'Inter', sans-serif; + cursor: pointer; + transition: all .2s; + text-decoration: none; + } + + .btn-lapor-header:hover { + background: rgba(239, 68, 68, .25); + } + + .btn-export { + width: 100%; + padding: 8px 12px; + background: rgba(59, 130, 246, .12); + border: 1px solid rgba(59, 130, 246, .3); + border-radius: 6px; + color: #60a5fa; + font-size: 11px; + font-weight: 600; + font-family: 'Inter', sans-serif; + cursor: pointer; + transition: all .2s; + margin: 0 14px 10px; + display: none; + } + + .btn-export:hover { + background: rgba(59, 130, 246, .22); + } + + .btn-logout { + padding: 6px 14px; + background: rgba(239, 68, 68, .12); + border: 1px solid rgba(239, 68, 68, .3); + border-radius: 6px; + color: #f87171; + font-size: 11px; + font-weight: 600; + font-family: 'Inter', sans-serif; + cursor: pointer; + transition: all .2s; + } + + .btn-logout:hover { + background: rgba(239, 68, 68, .22); + border-color: rgba(239, 68, 68, .5); + } + + /* ── Role-restricted element hiding ── */ + .hide-for-role { + display: none !important; + } + + .header-title { + display: flex; + align-items: center; + gap: 12px; + } + + .header-title h1 { + font-size: 16px; + font-weight: 700; + color: #fff; + margin: 0; + } + + .status-indicator { + display: flex; + align-items: center; + gap: 6px; + font-size: 11px; + font-weight: 600; + color: var(--accent-green); + } + + .pulse-dot { + width: 8px; + height: 8px; + background-color: var(--accent-green); + border-radius: 50%; + box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7); + animation: pulse 2s infinite; + } + + @keyframes pulse { + 0% { + transform: scale(0.95); + box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7); + } + + 70% { + transform: scale(1); + box-shadow: 0 0 0 6px rgba(16, 185, 129, 0); + } + + 100% { + transform: scale(0.95); + box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); + } + } + + #bottom-bar { + background: var(--bg-sidebar); + border-top: 1px solid var(--border); + padding: 8px 20px; + display: flex; + justify-content: flex-end; + align-items: center; + gap: 15px; + font-family: monospace; + font-size: 12px; + color: var(--text-secondary); + z-index: 1000; + } + + .fab-container { + position: fixed; + bottom: 50px; + right: 30px; + z-index: 9999; + display: flex; + flex-direction: column-reverse; + align-items: flex-end; + gap: 10px; + } + + .fab-btn { + width: 56px; + height: 56px; + border-radius: 50%; + background: linear-gradient(135deg, var(--accent-blue), #2563eb); + color: white; + border: none; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4); + font-size: 24px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s; + } + + .fab-btn:hover { + transform: scale(1.05); + background: linear-gradient(135deg, #60a5fa, var(--accent-blue)); + } + + .fab-menu { + display: none; + flex-direction: column; + gap: 8px; + align-items: flex-end; + margin-bottom: 10px; + } + + .fab-container:hover .fab-menu, + .fab-menu.active { + display: flex; + } + + .fab-item { + background: var(--bg-card); + border: 1px solid var(--border); + color: var(--text-primary); + padding: 10px 16px; + border-radius: 20px; + font-size: 12px; + font-weight: 600; + cursor: pointer; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); + transition: all 0.2s; + display: flex; + align-items: center; + gap: 8px; + } + + .fab-item:hover { + background: var(--bg-card-hover); + border-color: var(--accent-blue); + transform: translateX(-5px); + } + + #sidebar { + width: 320px; + min-width: 320px; + background: var(--bg-sidebar); + border-right: 1px solid var(--border); + display: flex; + flex-direction: column; + overflow: hidden; + z-index: 999; + } + + #map { + flex: 1; + position: relative; + z-index: 1; + } + + .sidebar-header { + padding: 20px 18px 16px; + border-bottom: 1px solid var(--border); + background: linear-gradient(135deg, #1e2a45 0%, #161b27 100%); + } + + .sidebar-header h1 { + font-size: 15px; + font-weight: 700; + letter-spacing: .3px; + } + + .sidebar-header p { + font-size: 11px; + color: var(--text-secondary); + margin-top: 3px; + } + + .sidebar-body { + flex: 1; + overflow-y: auto; + display: flex; + flex-direction: column; + } + + .section-title { + font-size: 10px; + font-weight: 700; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 1px; + padding: 14px 14px 6px; + } + + .btn-add { + display: none; + /* Disembunyikan karena diganti FAB */ + } + + .item-list { + padding: 0 10px 10px; + display: flex; + flex-direction: column; + gap: 6px; + } + + .item-card { + background: var(--bg-card); + border-radius: var(--radius-sm); + border: 1px solid var(--border); + padding: 10px 12px; + cursor: pointer; + transition: all .18s; + position: relative; + } + + .item-card:hover { + background: var(--bg-card-hover); + border-color: var(--accent-blue); + transform: translateX(2px); + } + + .item-card-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 6px; + } + + .item-card-name { + font-size: 12px; + font-weight: 600; + color: var(--text-primary); + flex: 1; + } + + .badge { + font-size: 9px; + font-weight: 700; + padding: 2px 7px; + border-radius: 20px; + text-transform: uppercase; + letter-spacing: .5px; + white-space: nowrap; + } + + + + .item-card-info { + font-size: 10px; + color: var(--text-secondary); + margin-top: 5px; + } + + .item-card-actions { + display: flex; + gap: 5px; + margin-top: 8px; + } + + .btn-sm { + flex: 1; + padding: 5px; + border: none; + border-radius: 4px; + font-size: 10px; + font-weight: 600; + cursor: pointer; + font-family: 'Inter', sans-serif; + transition: opacity .2s; + } + + .btn-sm:hover { + opacity: .8; + } + + .btn-zoom { + background: rgba(59, 130, 246, .2); + color: #60a5fa; + border: 1px solid rgba(59, 130, 246, .3); + } + + .btn-edit { + background: rgba(245, 158, 11, .2); + color: #fbbf24; + border: 1px solid rgba(245, 158, 11, .3); + } + + .btn-delete { + background: rgba(239, 68, 68, .2); + color: #f87171; + border: 1px solid rgba(239, 68, 68, .3); + } + + .legend-box { + padding: 12px 14px; + border-top: 1px solid var(--border); + background: var(--bg-dark); + } + + .legend-title { + font-size: 10px; + font-weight: 700; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 1px; + margin-bottom: 8px; + } + + .legend-item { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 5px; + font-size: 11px; + color: var(--text-secondary); + } + + .layer-info-box { + margin: 10px 14px; + padding: 12px; + background: rgba(59, 130, 246, .08); + border: 1px solid rgba(59, 130, 246, .25); + border-radius: var(--radius-sm); + } + + .layer-info-box p { + font-size: 11px; + color: var(--text-secondary); + line-height: 1.5; + } + + .layer-info-box strong { + color: var(--accent-blue); + } + + .divider { + height: 1px; + background: var(--border); + margin: 8px 0; + } + + .count-row { + display: flex; + gap: 8px; + padding: 0 14px 10px; + } + + .count-card { + flex: 1; + background: var(--bg-card); + border-radius: var(--radius-sm); + padding: 10px; + text-align: center; + border: 1px solid var(--border); + } + + .count-card .num { + font-size: 22px; + font-weight: 700; + } + + .count-card .lbl { + font-size: 10px; + color: var(--text-secondary); + margin-top: 2px; + } + + + + #map { + flex: 1; + height: 100vh; + } + + .leaflet-popup-content-wrapper { + background: var(--bg-card) !important; + border: 1px solid var(--border) !important; + border-radius: var(--radius-md) !important; + box-shadow: var(--shadow) !important; + color: var(--text-primary) !important; + padding: 0 !important; + } + + .leaflet-popup-tip { + background: var(--bg-card) !important; + } + + .leaflet-popup-close-button { + color: var(--text-secondary) !important; + top: 8px !important; + right: 8px !important; + } + + .leaflet-popup-content { + margin: 0 !important; + } + + .gis-popup { + padding: 16px; + min-width: 240px; + max-width: 300px; + } + + .popup-title { + font-size: 13px; + font-weight: 700; + margin-bottom: 12px; + padding-bottom: 8px; + border-bottom: 1px solid var(--border); + } + + .popup-form label { + display: block; + font-size: 10px; + font-weight: 600; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: .5px; + margin-bottom: 4px; + margin-top: 10px; + } + + .popup-form label:first-of-type { + margin-top: 0; + } + + .popup-form input, + .popup-form select { + width: 100%; + padding: 8px 10px; + background: var(--bg-dark); + border: 1px solid var(--border); + border-radius: var(--radius-sm); + color: var(--text-primary); + font-size: 12px; + font-family: 'Inter', sans-serif; + outline: none; + transition: border-color .2s; + } + + .popup-form input:focus, + .popup-form select:focus { + border-color: var(--accent-blue); + } + + .popup-actions { + display: flex; + gap: 6px; + margin-top: 12px; + } + + .popup-btn { + flex: 1; + padding: 8px; + border: none; + border-radius: var(--radius-sm); + font-size: 11px; + font-weight: 600; + font-family: 'Inter', sans-serif; + cursor: pointer; + transition: all .2s; + } + + .popup-btn:hover { + opacity: .85; + transform: translateY(-1px); + } + + .popup-btn-save { + background: var(--accent-green); + color: #fff; + } + + .popup-btn-edit { + background: var(--accent-yellow); + color: #000; + } + + .popup-btn-delete { + background: var(--accent-red); + color: #fff; + } + + .popup-btn-cancel { + background: var(--bg-dark); + color: var(--text-secondary); + border: 1px solid var(--border); + } + + .info-row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 5px 0; + border-bottom: 1px solid var(--border); + font-size: 11px; + } + + .info-row:last-child { + border-bottom: none; + } + + .info-label { + color: var(--text-secondary); + } + + .info-value { + color: var(--text-primary); + font-weight: 600; + } + + #toast { + position: fixed; + bottom: 30px; + right: 30px; + z-index: 10000; + display: flex; + flex-direction: column; + gap: 8px; + } + + .toast-item { + padding: 10px 18px; + border-radius: var(--radius-sm); + font-size: 12px; + font-weight: 500; + color: #fff; + box-shadow: var(--shadow); + animation: slideIn .3s ease; + max-width: 280px; + } + + .toast-success { + background: #065f46; + border: 1px solid #10b981; + } + + .toast-error { + background: #7f1d1d; + border: 1px solid #ef4444; + } + + .toast-info { + background: #1e3a5f; + border: 1px solid #3b82f6; + } + + @keyframes slideIn { + from { + opacity: 0; + transform: translateX(30px) + } + + to { + opacity: 1; + transform: translateX(0) + } + } + + ::-webkit-scrollbar { + width: 4px; + } + + ::-webkit-scrollbar-track { + background: transparent; + } + + ::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 2px; + } + + .loading-text { + text-align: center; + padding: 20px; + color: var(--text-muted); + font-size: 12px; + } + + /* Custom Leaflet Layer Control dark style */ + .leaflet-control-layers { + background: var(--bg-card) !important; + border: 1px solid var(--border) !important; + border-radius: var(--radius-md) !important; + color: var(--text-primary) !important; + } + + .leaflet-control-layers label { + color: var(--text-primary) !important; + font-size: 12px !important; + font-family: 'Inter', sans-serif !important; + } + + .leaflet-control-layers-separator { + border-top-color: var(--border) !important; + } + + .leaflet-control-layers-expanded { + padding: 10px 14px !important; + } + + .leaflet-control-layers-toggle { + background-color: var(--bg-card) !important; + border-radius: var(--radius-sm) !important; + } + + .btn-add-kemiskinan { + background: linear-gradient(135deg, #f97316, #c2410c); + color: #fff; + } + + .btn-add-masjid { + background: linear-gradient(135deg, #8b5cf6, #5b21b6); + color: #fff; + } + + .radius-wrap { + display: flex; + flex-direction: column; + gap: 4px; + margin-top: 6px; + } + + .radius-header { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 10px; + font-weight: 600; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: .5px; + } + + .radius-val { + color: var(--parsil); + font-weight: 700; + } + + .radius-slider { + width: 100%; + accent-color: var(--parsil); + cursor: pointer; + } + + .radius-ticks { + display: flex; + justify-content: space-between; + font-size: 9px; + color: var(--text-muted); + } + \ No newline at end of file diff --git a/WebGIS_PovertyMap/api_auth.php b/WebGIS_PovertyMap/api_auth.php new file mode 100644 index 0000000..939bac4 --- /dev/null +++ b/WebGIS_PovertyMap/api_auth.php @@ -0,0 +1,82 @@ + false, 'message' => 'Method tidak diizinkan.']); + exit(); +} + +// Baca body JSON +$body = json_decode(file_get_contents('php://input'), true); +if (!$body) { + http_response_code(400); + echo json_encode(['success' => false, 'message' => 'Request body tidak valid.']); + exit(); +} + +$username = isset($body['username']) ? trim($body['username']) : ''; +$password = isset($body['password']) ? $body['password'] : ''; +$roleReq = isset($body['role']) ? trim($body['role']) : ''; + +// Validasi input +if (!$username || !$password || !$roleReq) { + echo json_encode(['success' => false, 'message' => 'Username, password, dan role wajib diisi.']); + exit(); +} + +$validRoles = ['admin', 'pengurus', 'walikota']; +if (!in_array($roleReq, $validRoles)) { + echo json_encode(['success' => false, 'message' => 'Role tidak dikenali.']); + exit(); +} + +// ── Cari user di database ────────────────────────────────── +$db = getDB(); +$stmt = $db->prepare( + "SELECT id, username, nama_lengkap, role, password_hash + FROM users + WHERE username = ? AND role = ? + LIMIT 1" +); +$stmt->bind_param('ss', $username, $roleReq); +$stmt->execute(); +$result = $stmt->get_result(); + +if ($result->num_rows === 0) { + $stmt->close(); + $db->close(); + echo json_encode(['success' => false, 'message' => 'Username, password, atau peran tidak sesuai.']); + exit(); +} + +$user = $result->fetch_assoc(); +$stmt->close(); +$db->close(); + +// ── Verifikasi password ──────────────────────────────────── +if (!password_verify($password, $user['password_hash'])) { + echo json_encode(['success' => false, 'message' => 'Username, password, atau peran tidak sesuai.']); + exit(); +} + +// ── Login berhasil ───────────────────────────────────────── +echo json_encode([ + 'success' => true, + 'message' => 'Login berhasil.', + 'user' => [ + 'id' => (int)$user['id'], + 'username' => $user['username'], + 'nama' => $user['nama_lengkap'], + 'role' => $user['role'] + ] +]); +?> diff --git a/WebGIS_PovertyMap/api_bantuan.php b/WebGIS_PovertyMap/api_bantuan.php new file mode 100644 index 0000000..93d547e --- /dev/null +++ b/WebGIS_PovertyMap/api_bantuan.php @@ -0,0 +1,129 @@ + 'Parameter id_kemiskinan wajib diisi']); + exit(); + } + + $db = getDB(); + $stmt = $db->prepare("SELECT * FROM riwayat_bantuan WHERE id_kemiskinan = ? ORDER BY tanggal DESC, created_at DESC"); + $stmt->bind_param('i', $id_kemiskinan); + $stmt->execute(); + + $result = $stmt->get_result(); + $data = []; + while ($row = $result->fetch_assoc()) { + $data[] = $row; + } + + echo json_encode($data); + $db->close(); + exit(); +} + +// POST: Tambah riwayat bantuan baru +if ($method === 'POST') { + $body = json_decode(file_get_contents('php://input'), true); + + $id_kemiskinan = isset($body['id_kemiskinan']) ? (int)$body['id_kemiskinan'] : 0; + $pemberi = isset($body['pemberi']) ? trim($body['pemberi']) : ''; + $jenis_bantuan = isset($body['jenis_bantuan']) ? trim($body['jenis_bantuan']) : ''; + $tanggal = isset($body['tanggal']) ? trim($body['tanggal']) : ''; + $keterangan = isset($body['keterangan']) ? trim($body['keterangan']) : ''; + + if (!$id_kemiskinan || !$pemberi || !$jenis_bantuan || !$tanggal) { + http_response_code(400); + echo json_encode(['error' => 'id_kemiskinan, pemberi, jenis_bantuan, dan tanggal wajib diisi']); + exit(); + } + + $db = getDB(); + try { + $db->begin_transaction(); + + // 1. Insert ke riwayat_bantuan + $stmt = $db->prepare("INSERT INTO riwayat_bantuan (id_kemiskinan, pemberi, jenis_bantuan, tanggal, keterangan) VALUES (?, ?, ?, ?, ?)"); + $stmt->bind_param('issss', $id_kemiskinan, $pemberi, $jenis_bantuan, $tanggal, $keterangan); + $stmt->execute(); + $id_riwayat = $db->insert_id; + + // 2. Update status_bantuan di tabel data_kemiskinan menjadi 'Sudah' + $stmtUpdate = $db->prepare("UPDATE data_kemiskinan SET status_bantuan = 'Sudah' WHERE id = ?"); + $stmtUpdate->bind_param('i', $id_kemiskinan); + $stmtUpdate->execute(); + + $db->commit(); + + echo json_encode([ + 'success' => true, + 'message' => 'Berhasil menambahkan catatan bantuan', + 'id' => $id_riwayat, + 'status_bantuan' => 'Sudah' + ]); + } catch (Exception $e) { + $db->rollback(); + http_response_code(500); + echo json_encode(['error' => 'Gagal menyimpan data: ' . $e->getMessage()]); + } + $db->close(); + exit(); +} + +// DELETE: Hapus catatan bantuan +if ($method === 'DELETE') { + $id = isset($_GET['id']) ? (int)$_GET['id'] : 0; + $id_kemiskinan = isset($_GET['id_kemiskinan']) ? (int)$_GET['id_kemiskinan'] : 0; + + if (!$id) { + http_response_code(400); + echo json_encode(['error' => 'Parameter id wajib diisi']); + exit(); + } + + $db = getDB(); + try { + $db->begin_transaction(); + + $stmt = $db->prepare("DELETE FROM riwayat_bantuan WHERE id = ?"); + $stmt->bind_param('i', $id); + $stmt->execute(); + + // Cek apakah masih ada bantuan lain + $stmtCek = $db->prepare("SELECT COUNT(*) AS total FROM riwayat_bantuan WHERE id_kemiskinan = ?"); + $stmtCek->bind_param('i', $id_kemiskinan); + $stmtCek->execute(); + $res = $stmtCek->get_result()->fetch_assoc(); + $count = $res['total']; + + $status_baru = 'Sudah'; + if ($count == 0) { + $stmtUpdate = $db->prepare("UPDATE data_kemiskinan SET status_bantuan = 'Belum' WHERE id = ?"); + $stmtUpdate->bind_param('i', $id_kemiskinan); + $stmtUpdate->execute(); + $status_baru = 'Belum'; + } + + $db->commit(); + echo json_encode(['success' => true, 'status_bantuan' => $status_baru]); + } catch (Exception $e) { + $db->rollback(); + http_response_code(500); + echo json_encode(['error' => 'Gagal menghapus data']); + } + $db->close(); + exit(); +} + +http_response_code(405); +echo json_encode(['error' => 'Method tidak diizinkan']); diff --git a/WebGIS_PovertyMap/api_dashboard.php b/WebGIS_PovertyMap/api_dashboard.php new file mode 100644 index 0000000..0287444 --- /dev/null +++ b/WebGIS_PovertyMap/api_dashboard.php @@ -0,0 +1,141 @@ + 'Method tidak diizinkan']); + exit(); +} + +$action = isset($_GET['action']) ? $_GET['action'] : 'stats'; +$db = getDB(); + +// ── STATISTIK RINGKASAN ──────────────────────────────────── +if ($action === 'stats') { + $stats = []; + + // Total per kategori kemiskinan + $result = $db->query("SELECT kategori, COUNT(*) as total, + SUM(CASE WHEN status_bantuan = 'Sudah' THEN 1 ELSE 0 END) as sudah_dibantu, + SUM(CASE WHEN status_bantuan != 'Sudah' OR status_bantuan IS NULL THEN 1 ELSE 0 END) as belum_dibantu + FROM data_kemiskinan GROUP BY kategori"); + $kategori = []; + $total_warga = 0; + $total_sudah = 0; + $total_belum = 0; + while ($row = $result->fetch_assoc()) { + $row['total'] = (int)$row['total']; + $row['sudah_dibantu'] = (int)$row['sudah_dibantu']; + $row['belum_dibantu'] = (int)$row['belum_dibantu']; + $kategori[] = $row; + $total_warga += $row['total']; + $total_sudah += $row['sudah_dibantu']; + $total_belum += $row['belum_dibantu']; + } + $stats['kategori'] = $kategori; + $stats['total_warga'] = $total_warga; + $stats['total_sudah'] = $total_sudah; + $stats['total_belum'] = $total_belum; + $stats['persen_sudah'] = $total_warga > 0 ? round(($total_sudah / $total_warga) * 100, 1) : 0; + + // Total rumah ibadah + $result = $db->query("SELECT jenis, COUNT(*) as total FROM data_masjid GROUP BY jenis"); + $ibadah = []; + $total_ibadah = 0; + while ($row = $result->fetch_assoc()) { + $row['total'] = (int)$row['total']; + $ibadah[] = $row; + $total_ibadah += $row['total']; + } + $stats['rumah_ibadah'] = $ibadah; + $stats['total_ibadah'] = $total_ibadah; + + // Total bantuan tercatat + $result = $db->query("SELECT COUNT(*) as total FROM riwayat_bantuan"); + $row = $result->fetch_assoc(); + $stats['total_bantuan'] = (int)$row['total']; + + // Total laporan masyarakat per status + $result = $db->query("SELECT status, COUNT(*) as total FROM laporan_masyarakat GROUP BY status"); + $laporan_status = []; + while ($row = $result->fetch_assoc()) { + $row['total'] = (int)$row['total']; + $laporan_status[] = $row; + } + $stats['laporan_status'] = $laporan_status; + + // Penyakit terbanyak (aggregate) + $result = $db->query("SELECT riwayat_penyakit, COUNT(*) as total + FROM data_kemiskinan + WHERE riwayat_penyakit IS NOT NULL AND riwayat_penyakit != '' + GROUP BY riwayat_penyakit + ORDER BY total DESC LIMIT 10"); + $penyakit = []; + while ($row = $result->fetch_assoc()) { + $row['total'] = (int)$row['total']; + $penyakit[] = $row; + } + $stats['penyakit_terbanyak'] = $penyakit; + + echo json_encode($stats); + $db->close(); + exit(); +} + +// ── LAPORAN TERBARU ──────────────────────────────────────── +if ($action === 'laporan_terbaru') { + $limit = isset($_GET['limit']) ? min(50, max(1, (int)$_GET['limit'])) : 20; + $result = $db->query("SELECT id, nama_pelapor, nik, no_wa, deskripsi, latitude, longitude, status, alasan_tolak, created_at + FROM laporan_masyarakat ORDER BY created_at DESC LIMIT $limit"); + $rows = []; + while ($row = $result->fetch_assoc()) { + $row['latitude'] = (float)$row['latitude']; + $row['longitude'] = (float)$row['longitude']; + $rows[] = $row; + } + echo json_encode($rows); + $db->close(); + exit(); +} + +// ── DATA KEMISKINAN UNTUK TABEL ──────────────────────────── +if ($action === 'kemiskinan') { + $result = $db->query("SELECT id, nama_kk, nik, kategori, status_bantuan, jumlah_kk, alamat, pendidikan, riwayat_penyakit, latitude, longitude + FROM data_kemiskinan ORDER BY id DESC"); + $rows = []; + while ($row = $result->fetch_assoc()) { + $row['jumlah_kk'] = (int)$row['jumlah_kk']; + $row['latitude'] = (float)$row['latitude']; + $row['longitude'] = (float)$row['longitude']; + $rows[] = $row; + } + echo json_encode($rows); + $db->close(); + exit(); +} + +// ── HEATMAP DATA ─────────────────────────────────────────── +if ($action === 'heatmap') { + $result = $db->query("SELECT latitude, longitude, kategori FROM data_kemiskinan"); + $points = []; + while ($row = $result->fetch_assoc()) { + $intensity = 0.5; + if ($row['kategori'] === 'Sangat Miskin') $intensity = 1.0; + else if ($row['kategori'] === 'Miskin') $intensity = 0.7; + $points[] = [(float)$row['latitude'], (float)$row['longitude'], $intensity]; + } + echo json_encode($points); + $db->close(); + exit(); +} + +http_response_code(400); +echo json_encode(['error' => 'Action tidak dikenali. Gunakan: stats, laporan_terbaru, kemiskinan, heatmap']); +?> diff --git a/WebGIS_PovertyMap/api_jalan.php b/WebGIS_PovertyMap/api_jalan.php new file mode 100644 index 0000000..bf35c86 --- /dev/null +++ b/WebGIS_PovertyMap/api_jalan.php @@ -0,0 +1,165 @@ +prepare("SELECT id, nama_jalan, status, panjang_m, geojson FROM data_jalan WHERE id = ?"); + $stmt->bind_param('i', $id); + $stmt->execute(); + $result = $stmt->get_result()->fetch_assoc(); + if ($result) { + $result['geojson'] = json_decode($result['geojson']); // decode jadi object + echo json_encode($result); + } else { + http_response_code(404); + echo json_encode(['error' => 'Data tidak ditemukan']); + } + } else { + // Get all + $result = $db->query("SELECT id, nama_jalan, status, panjang_m, geojson FROM data_jalan ORDER BY id DESC"); + $rows = []; + while ($row = $result->fetch_assoc()) { + $row['geojson'] = json_decode($row['geojson']); + $rows[] = $row; + } + echo json_encode($rows); + } + $db->close(); + exit(); +} + +// ---- POST: Tambah data jalan baru ---- +if ($method === 'POST') { + $input = json_decode(file_get_contents('php://input'), true); + + if (empty($input['nama_jalan']) || empty($input['status']) || empty($input['geojson'])) { + http_response_code(400); + echo json_encode(['error' => 'Data tidak lengkap. Field wajib: nama_jalan, status, geojson']); + exit(); + } + + $nama_jalan = trim($input['nama_jalan']); + $status = $input['status']; + $panjang_m = isset($input['panjang_m']) ? (float)$input['panjang_m'] : 0; + $geojson = json_encode($input['geojson']); // simpan sebagai string JSON + + // Validasi status + $valid_status = ['Nasional', 'Provinsi', 'Kabupaten']; + if (!in_array($status, $valid_status)) { + http_response_code(400); + echo json_encode(['error' => 'Status tidak valid. Pilih: Nasional, Provinsi, atau Kabupaten']); + exit(); + } + + $db = getDB(); + $stmt = $db->prepare("INSERT INTO data_jalan (nama_jalan, status, panjang_m, geojson) VALUES (?, ?, ?, ?)"); + $stmt->bind_param('ssds', $nama_jalan, $status, $panjang_m, $geojson); + + if ($stmt->execute()) { + $new_id = $db->insert_id; + http_response_code(201); + echo json_encode([ + 'message' => 'Data jalan berhasil disimpan', + 'id' => $new_id, + 'nama_jalan'=> $nama_jalan, + 'status' => $status, + 'panjang_m' => $panjang_m + ]); + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal menyimpan data: ' . $stmt->error]); + } + $db->close(); + exit(); +} + +// ---- PUT: Update data jalan ---- +if ($method === 'PUT') { + if (!$id) { + http_response_code(400); + echo json_encode(['error' => 'ID diperlukan untuk update']); + exit(); + } + + $input = json_decode(file_get_contents('php://input'), true); + $db = getDB(); + + // Cek apakah hanya update geometri (saat edit di peta) atau full update + if (isset($input['geojson']) && !isset($input['nama_jalan'])) { + // Update hanya geometri & panjang (drag/edit di peta) + $geojson = json_encode($input['geojson']); + $panjang_m = isset($input['panjang_m']) ? (float)$input['panjang_m'] : 0; + $stmt = $db->prepare("UPDATE data_jalan SET geojson = ?, panjang_m = ? WHERE id = ?"); + $stmt->bind_param('sdi', $geojson, $panjang_m, $id); + } else { + // Full update (edit atribut + geometri) + $nama_jalan = trim($input['nama_jalan'] ?? ''); + $status = $input['status'] ?? ''; + $panjang_m = isset($input['panjang_m']) ? (float)$input['panjang_m'] : 0; + $geojson = json_encode($input['geojson'] ?? []); + + $valid_status = ['Nasional', 'Provinsi', 'Kabupaten']; + if (!in_array($status, $valid_status)) { + http_response_code(400); + echo json_encode(['error' => 'Status tidak valid']); + exit(); + } + + $stmt = $db->prepare("UPDATE data_jalan SET nama_jalan = ?, status = ?, panjang_m = ?, geojson = ? WHERE id = ?"); + $stmt->bind_param('ssdsi', $nama_jalan, $status, $panjang_m, $geojson, $id); + } + + if ($stmt->execute()) { + echo json_encode(['message' => 'Data jalan berhasil diperbarui', 'id' => $id]); + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal memperbarui data: ' . $stmt->error]); + } + $db->close(); + exit(); +} + +// ---- DELETE: Hapus data jalan ---- +if ($method === 'DELETE') { + if (!$id) { + http_response_code(400); + echo json_encode(['error' => 'ID diperlukan untuk hapus']); + exit(); + } + + $db = getDB(); + $stmt = $db->prepare("DELETE FROM data_jalan WHERE id = ?"); + $stmt->bind_param('i', $id); + + if ($stmt->execute()) { + if ($stmt->affected_rows > 0) { + echo json_encode(['message' => 'Data jalan berhasil dihapus', 'id' => $id]); + } else { + http_response_code(404); + echo json_encode(['error' => 'Data tidak ditemukan']); + } + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal menghapus data: ' . $stmt->error]); + } + $db->close(); + exit(); +} + +// Method tidak dikenal +http_response_code(405); +echo json_encode(['error' => 'Method tidak diizinkan']); +?> diff --git a/WebGIS_PovertyMap/api_kemiskinan.php b/WebGIS_PovertyMap/api_kemiskinan.php new file mode 100644 index 0000000..c824c9c --- /dev/null +++ b/WebGIS_PovertyMap/api_kemiskinan.php @@ -0,0 +1,192 @@ +prepare("SELECT id, nik, nama_kk, jumlah_kk, alamat, latitude, longitude, status_bantuan, kategori, skor_penghasilan, skor_rumah, skor_makan, tanggal_lahir, pendidikan, riwayat_penyakit FROM data_kemiskinan WHERE id = ?"); + $stmt->bind_param('i', $id); + $stmt->execute(); + $result = $stmt->get_result()->fetch_assoc(); + if ($result) { + // Cast tipe + $result['jumlah_kk'] = (int)$result['jumlah_kk']; + $result['latitude'] = (float)$result['latitude']; + $result['longitude'] = (float)$result['longitude']; + echo json_encode($result); + } else { + http_response_code(404); + echo json_encode(['error' => 'Data kemiskinan tidak ditemukan']); + } + } else { + $result = $db->query("SELECT id, nik, nama_kk, jumlah_kk, alamat, latitude, longitude, status_bantuan, kategori, skor_penghasilan, skor_rumah, skor_makan, tanggal_lahir, pendidikan, riwayat_penyakit FROM data_kemiskinan ORDER BY id DESC"); + $rows = []; + while ($row = $result->fetch_assoc()) { + $row['jumlah_kk'] = (int)$row['jumlah_kk']; + $row['latitude'] = (float)$row['latitude']; + $row['longitude'] = (float)$row['longitude']; + $rows[] = $row; + } + echo json_encode($rows); + } + $db->close(); + exit(); +} + +// ---- POST: Tambah data kemiskinan baru ---- +if ($method === 'POST') { + $input = json_decode(file_get_contents('php://input'), true); + + if (empty($input['nama_kk']) || !isset($input['jumlah_kk']) || empty($input['alamat']) + || !isset($input['latitude']) || !isset($input['longitude'])) { + http_response_code(400); + echo json_encode(['error' => 'Data tidak lengkap. Field wajib: nama_kk, jumlah_kk, alamat, latitude, longitude']); + exit(); + } + + $nama_kk = trim($input['nama_kk']); + $nik = trim($input['nik'] ?? ''); + $jumlah_kk = max(1, (int)$input['jumlah_kk']); + $alamat = trim($input['alamat']); + $latitude = (float)$input['latitude']; + $longitude = (float)$input['longitude']; + + $tanggal_lahir = isset($input['tanggal_lahir']) && $input['tanggal_lahir'] !== '' ? $input['tanggal_lahir'] : null; + $pendidikan = isset($input['pendidikan']) ? trim($input['pendidikan']) : null; + $riwayat_penyakit = isset($input['riwayat_penyakit']) ? trim($input['riwayat_penyakit']) : null; + + $kategori = isset($input['kategori']) ? trim($input['kategori']) : 'Miskin'; + $skor_penghasilan = isset($input['skor_penghasilan']) ? (int)$input['skor_penghasilan'] : 0; + $skor_rumah = isset($input['skor_rumah']) ? (int)$input['skor_rumah'] : 0; + $skor_makan = isset($input['skor_makan']) ? (int)$input['skor_makan'] : 0; + + $db = getDB(); + $stmt = $db->prepare("INSERT INTO data_kemiskinan (nama_kk, nik, jumlah_kk, alamat, latitude, longitude, kategori, skor_penghasilan, skor_rumah, skor_makan, tanggal_lahir, pendidikan, riwayat_penyakit) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + $stmt->bind_param('ssisddsiiisss', $nama_kk, $nik, $jumlah_kk, $alamat, $latitude, $longitude, $kategori, $skor_penghasilan, $skor_rumah, $skor_makan, $tanggal_lahir, $pendidikan, $riwayat_penyakit); + + if ($stmt->execute()) { + $new_id = $db->insert_id; + http_response_code(201); + echo json_encode([ + 'message' => 'Data kemiskinan berhasil disimpan', + 'id' => $new_id, + 'nama_kk' => $nama_kk, + 'nik' => $nik, + 'jumlah_kk' => $jumlah_kk, + 'alamat' => $alamat, + 'latitude' => $latitude, + 'longitude' => $longitude, + 'status_bantuan' => 'Belum', + 'kategori' => $kategori, + 'skor_penghasilan' => $skor_penghasilan, + 'skor_rumah' => $skor_rumah, + 'skor_makan' => $skor_makan, + 'tanggal_lahir' => $tanggal_lahir, + 'pendidikan' => $pendidikan, + 'riwayat_penyakit' => $riwayat_penyakit + ]); + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal menyimpan data: ' . $stmt->error]); + } + $db->close(); + exit(); +} + +// ---- PUT: Update data kemiskinan ---- +if ($method === 'PUT') { + if (!$id) { + http_response_code(400); + echo json_encode(['error' => 'ID diperlukan untuk update']); + exit(); + } + + $input = json_decode(file_get_contents('php://input'), true); + $db = getDB(); + + // Update posisi saja (drag marker) + if (isset($input['latitude']) && isset($input['longitude']) && !isset($input['nama_kk'])) { + $latitude = (float)$input['latitude']; + $longitude = (float)$input['longitude']; + $stmt = $db->prepare("UPDATE data_kemiskinan SET latitude = ?, longitude = ? WHERE id = ?"); + $stmt->bind_param('ddi', $latitude, $longitude, $id); + } else { + // Full update + $nama_kk = trim($input['nama_kk'] ?? ''); + $nik = trim($input['nik'] ?? ''); + $jumlah_kk = max(1, (int)($input['jumlah_kk'] ?? 1)); + $alamat = trim($input['alamat'] ?? ''); + $latitude = (float)($input['latitude'] ?? 0); + $longitude = (float)($input['longitude'] ?? 0); + + $tanggal_lahir = isset($input['tanggal_lahir']) && $input['tanggal_lahir'] !== '' ? $input['tanggal_lahir'] : null; + $pendidikan = isset($input['pendidikan']) ? trim($input['pendidikan']) : null; + $riwayat_penyakit = isset($input['riwayat_penyakit']) ? trim($input['riwayat_penyakit']) : null; + + $kategori = isset($input['kategori']) ? trim($input['kategori']) : 'Miskin'; + $skor_penghasilan = isset($input['skor_penghasilan']) ? (int)$input['skor_penghasilan'] : 0; + $skor_rumah = isset($input['skor_rumah']) ? (int)$input['skor_rumah'] : 0; + $skor_makan = isset($input['skor_makan']) ? (int)$input['skor_makan'] : 0; + + if (empty($nama_kk) || empty($alamat)) { + http_response_code(400); + echo json_encode(['error' => 'nama_kk dan alamat wajib diisi']); + exit(); + } + + $stmt = $db->prepare("UPDATE data_kemiskinan SET nama_kk = ?, nik = ?, jumlah_kk = ?, alamat = ?, latitude = ?, longitude = ?, kategori = ?, skor_penghasilan = ?, skor_rumah = ?, skor_makan = ?, tanggal_lahir = ?, pendidikan = ?, riwayat_penyakit = ? WHERE id = ?"); + $stmt->bind_param('ssisddsiiisssi', $nama_kk, $nik, $jumlah_kk, $alamat, $latitude, $longitude, $kategori, $skor_penghasilan, $skor_rumah, $skor_makan, $tanggal_lahir, $pendidikan, $riwayat_penyakit, $id); + } + + if ($stmt->execute()) { + echo json_encode(['message' => 'Data kemiskinan berhasil diperbarui', 'id' => $id]); + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal memperbarui data: ' . $stmt->error]); + } + $db->close(); + exit(); +} + +// ---- DELETE: Hapus data kemiskinan ---- +if ($method === 'DELETE') { + if (!$id) { + http_response_code(400); + echo json_encode(['error' => 'ID diperlukan untuk hapus']); + exit(); + } + + $db = getDB(); + $stmt = $db->prepare("DELETE FROM data_kemiskinan WHERE id = ?"); + $stmt->bind_param('i', $id); + + if ($stmt->execute()) { + if ($stmt->affected_rows > 0) { + echo json_encode(['message' => 'Data kemiskinan berhasil dihapus', 'id' => $id]); + } else { + http_response_code(404); + echo json_encode(['error' => 'Data tidak ditemukan']); + } + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal menghapus data: ' . $stmt->error]); + } + $db->close(); + exit(); +} + +// Method tidak dikenal +http_response_code(405); +echo json_encode(['error' => 'Method tidak diizinkan']); +?> diff --git a/WebGIS_PovertyMap/api_lapor.php b/WebGIS_PovertyMap/api_lapor.php new file mode 100644 index 0000000..fb3dd6b --- /dev/null +++ b/WebGIS_PovertyMap/api_lapor.php @@ -0,0 +1,218 @@ +prepare("SELECT id, nama_pelapor, nik, no_wa, deskripsi, latitude, longitude, status, alasan_tolak, created_at, tanggal_lahir, pendidikan, riwayat_penyakit, skor_penghasilan, skor_rumah, skor_makan, jumlah_kk FROM laporan_masyarakat WHERE id = ?"); + $stmt->bind_param('i', $id); + $stmt->execute(); + $result = $stmt->get_result()->fetch_assoc(); + if ($result) { + $result['latitude'] = (float)$result['latitude']; + $result['longitude'] = (float)$result['longitude']; + echo json_encode($result); + } else { + http_response_code(404); + echo json_encode(['error' => 'Laporan tidak ditemukan']); + } + } else { + $result = $db->query("SELECT id, nama_pelapor, nik, no_wa, deskripsi, latitude, longitude, status, alasan_tolak, created_at, tanggal_lahir, pendidikan, riwayat_penyakit, skor_penghasilan, skor_rumah, skor_makan, jumlah_kk FROM laporan_masyarakat ORDER BY id DESC"); + $rows = []; + while ($row = $result->fetch_assoc()) { + $row['latitude'] = (float)$row['latitude']; + $row['longitude'] = (float)$row['longitude']; + $rows[] = $row; + } + echo json_encode($rows); + } + $db->close(); + exit(); +} + +// ---- POST: Tambah laporan baru ---- +if ($method === 'POST') { + $input = json_decode(file_get_contents('php://input'), true); + + if (empty($input['nama_pelapor']) || empty($input['deskripsi']) || !isset($input['latitude']) || !isset($input['longitude'])) { + http_response_code(400); + echo json_encode(['error' => 'Data tidak lengkap. Field wajib: nama_pelapor, deskripsi, latitude, longitude']); + exit(); + } + + $nama = trim($input['nama_pelapor']); + $nik = isset($input['nik']) && $input['nik'] ? trim($input['nik']) : null; + $no_wa = isset($input['no_wa']) && $input['no_wa'] ? trim($input['no_wa']) : null; + $desk = trim($input['deskripsi']); + $lat = (float)$input['latitude']; + $lng = (float)$input['longitude']; + + // New Fields + $tgl_lahir = isset($input['tanggal_lahir']) && $input['tanggal_lahir'] ? $input['tanggal_lahir'] : null; + $pdd = isset($input['pendidikan']) && $input['pendidikan'] ? $input['pendidikan'] : null; + $pny = isset($input['riwayat_penyakit']) && $input['riwayat_penyakit'] ? $input['riwayat_penyakit'] : null; + $sp = isset($input['skor_penghasilan']) ? (int)$input['skor_penghasilan'] : null; + $sr = isset($input['skor_rumah']) ? (int)$input['skor_rumah'] : null; + $sm = isset($input['skor_makan']) ? (int)$input['skor_makan'] : null; + $jml = isset($input['jumlah_kk']) ? (int)$input['jumlah_kk'] : 1; + + // Validasi NIK 16 digit jika diisi + if ($nik && strlen($nik) !== 16) { + http_response_code(400); + echo json_encode(['error' => 'NIK harus 16 digit']); + exit(); + } + + $db = getDB(); + $stmt = $db->prepare("INSERT INTO laporan_masyarakat (nama_pelapor, nik, no_wa, deskripsi, latitude, longitude, tanggal_lahir, pendidikan, riwayat_penyakit, skor_penghasilan, skor_rumah, skor_makan, jumlah_kk) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + $stmt->bind_param('ssssddsssiiii', $nama, $nik, $no_wa, $desk, $lat, $lng, $tgl_lahir, $pdd, $pny, $sp, $sr, $sm, $jml); + + if ($stmt->execute()) { + $new_id = $db->insert_id; + http_response_code(201); + echo json_encode([ + 'message' => 'Laporan berhasil dikirim', + 'id' => $new_id, + 'status' => 'Pending' + ]); + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal menyimpan laporan: ' . $stmt->error]); + } + $db->close(); + exit(); +} + +// ---- PUT: Update status laporan ---- +if ($method === 'PUT') { + if (!$id) { + http_response_code(400); + echo json_encode(['error' => 'ID wajib disertakan untuk update']); + exit(); + } + + $input = json_decode(file_get_contents('php://input'), true); + if (empty($input['status'])) { + http_response_code(400); + echo json_encode(['error' => 'Status wajib diisi (Pending/Diproses/Diverifikasi/Ditolak/Selesai)']); + exit(); + } + + $status = $input['status']; + $action = isset($input['action']) ? $input['action'] : null; + $alasan_tolak = isset($input['alasan_tolak']) ? trim($input['alasan_tolak']) : null; + + $db = getDB(); + + // === VERIFIKASI: Konversi laporan menjadi data kemiskinan resmi === + if ($action === 'verify' && $status === 'Diverifikasi') { + // Ambil data laporan dulu + $stmtGet = $db->prepare("SELECT * FROM laporan_masyarakat WHERE id = ?"); + $stmtGet->bind_param('i', $id); + $stmtGet->execute(); + $laporan = $stmtGet->get_result()->fetch_assoc(); + + if (!$laporan) { + http_response_code(404); + echo json_encode(['error' => 'Laporan tidak ditemukan']); + $db->close(); + exit(); + } + + $db->begin_transaction(); + try { + // Hitung kategori dari skor (sama dengan logika frontend) + $sp = (int)$laporan['skor_penghasilan']; + $sr = (int)$laporan['skor_rumah']; + $sm = (int)$laporan['skor_makan']; + $total_skor = $sp + $sr + $sm; + + $kategori = 'Miskin'; + if ($total_skor >= 7) { + $kategori = 'Sangat Miskin'; + } else if ($total_skor >= 4) { + $kategori = 'Miskin'; + } else { + $kategori = 'Hampir Miskin'; + } + + // 1. Masukkan ke data_kemiskinan sebagai data resmi + $stmtInsert = $db->prepare( + "INSERT INTO data_kemiskinan (nama_kk, nik, alamat, latitude, longitude, kategori, status_bantuan, tanggal_lahir, pendidikan, riwayat_penyakit, skor_penghasilan, skor_rumah, skor_makan, jumlah_kk) + VALUES (?, ?, ?, ?, ?, ?, 'Belum', ?, ?, ?, ?, ?, ?, ?)" + ); + $alamat = $laporan['deskripsi']; // Use deskripsi directly as address/notes + $stmtInsert->bind_param('sssddssssiiii', + $laporan['nama_pelapor'], + $laporan['nik'], + $alamat, + $laporan['latitude'], + $laporan['longitude'], + $kategori, + $laporan['tanggal_lahir'], + $laporan['pendidikan'], + $laporan['riwayat_penyakit'], + $laporan['skor_penghasilan'], + $laporan['skor_rumah'], + $laporan['skor_makan'], + $laporan['jumlah_kk'] + ); + $stmtInsert->execute(); + $new_kemiskinan_id = $db->insert_id; + + // 2. Update status laporan menjadi Diverifikasi + $stmtUpdate = $db->prepare("UPDATE laporan_masyarakat SET status = 'Diverifikasi' WHERE id = ?"); + $stmtUpdate->bind_param('i', $id); + $stmtUpdate->execute(); + + $db->commit(); + echo json_encode([ + 'message' => 'Laporan diverifikasi dan data kemiskinan resmi telah dibuat', + 'id_kemiskinan' => $new_kemiskinan_id + ]); + } catch (Exception $e) { + $db->rollback(); + http_response_code(500); + echo json_encode(['error' => 'Gagal memverifikasi: ' . $e->getMessage()]); + } + $db->close(); + exit(); + } + + // === TOLAK: Update status + alasan === + if ($status === 'Ditolak') { + $stmt = $db->prepare("UPDATE laporan_masyarakat SET status = ?, alasan_tolak = ? WHERE id = ?"); + $stmt->bind_param('ssi', $status, $alasan_tolak, $id); + } else { + // === Update status biasa === + $stmt = $db->prepare("UPDATE laporan_masyarakat SET status = ? WHERE id = ?"); + $stmt->bind_param('si', $status, $id); + } + + if ($stmt->execute()) { + if ($stmt->affected_rows > 0) { + echo json_encode(['message' => 'Status laporan berhasil diupdate']); + } else { + http_response_code(404); + echo json_encode(['error' => 'Laporan tidak ditemukan atau tidak ada perubahan']); + } + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal mengupdate laporan']); + } + $db->close(); + exit(); +} +?> diff --git a/WebGIS_PovertyMap/api_masjid.php b/WebGIS_PovertyMap/api_masjid.php new file mode 100644 index 0000000..1055497 --- /dev/null +++ b/WebGIS_PovertyMap/api_masjid.php @@ -0,0 +1,182 @@ +prepare("SELECT id, nama_masjid, jenis, pic_masjid, alamat, radius_m, latitude, longitude FROM data_masjid WHERE id = ?"); + $stmt->bind_param('i', $id); + $stmt->execute(); + $result = $stmt->get_result()->fetch_assoc(); + if ($result) { + $result['radius_m'] = (int)$result['radius_m']; + $result['latitude'] = (float)$result['latitude']; + $result['longitude'] = (float)$result['longitude']; + echo json_encode($result); + } else { + http_response_code(404); + echo json_encode(['error' => 'Data masjid tidak ditemukan']); + } + } else { + $result = $db->query("SELECT id, nama_masjid, jenis, pic_masjid, alamat, radius_m, latitude, longitude FROM data_masjid ORDER BY id DESC"); + $rows = []; + while ($row = $result->fetch_assoc()) { + $row['radius_m'] = (int)$row['radius_m']; + $row['latitude'] = (float)$row['latitude']; + $row['longitude'] = (float)$row['longitude']; + $rows[] = $row; + } + echo json_encode($rows); + } + $db->close(); + exit(); +} + +// ---- POST: Tambah masjid baru ---- +if ($method === 'POST') { + $input = json_decode(file_get_contents('php://input'), true); + + if (empty($input['nama_masjid']) || empty($input['pic_masjid']) || empty($input['alamat']) + || !isset($input['latitude']) || !isset($input['longitude'])) { + http_response_code(400); + echo json_encode(['error' => 'Data tidak lengkap. Field wajib: nama_masjid, jenis, pic_masjid, alamat, latitude, longitude']); + exit(); + } + + $nama_masjid = trim($input['nama_masjid']); + $jenis = isset($input['jenis']) ? trim($input['jenis']) : 'Masjid'; + $pic_masjid = trim($input['pic_masjid']); + $alamat = trim($input['alamat']); + $radius_m = isset($input['radius_m']) ? max(100, min(5000, (int)$input['radius_m'])) : 500; + $latitude = (float)$input['latitude']; + $longitude = (float)$input['longitude']; + + $valid_jenis = ['Masjid','Gereja','Pura','Vihara','Klenteng']; + if (!in_array($jenis, $valid_jenis)) $jenis = 'Masjid'; + + $db = getDB(); + $stmt = $db->prepare("INSERT INTO data_masjid (nama_masjid, jenis, pic_masjid, alamat, radius_m, latitude, longitude) VALUES (?, ?, ?, ?, ?, ?, ?)"); + $stmt->bind_param('ssssidd', $nama_masjid, $jenis, $pic_masjid, $alamat, $radius_m, $latitude, $longitude); + + if ($stmt->execute()) { + $new_id = $db->insert_id; + http_response_code(201); + echo json_encode([ + 'message' => 'Data ibadah berhasil disimpan', + 'id' => $new_id, + 'nama_masjid' => $nama_masjid, + 'jenis' => $jenis, + 'pic_masjid' => $pic_masjid, + 'alamat' => $alamat, + 'radius_m' => $radius_m, + 'latitude' => $latitude, + 'longitude' => $longitude + ]); + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal menyimpan data: ' . $stmt->error]); + } + $db->close(); + exit(); +} + +// ---- PUT: Update masjid ---- +if ($method === 'PUT') { + if (!$id) { + http_response_code(400); + echo json_encode(['error' => 'ID diperlukan untuk update']); + exit(); + } + + $input = json_decode(file_get_contents('php://input'), true); + $db = getDB(); + + // Update posisi saja (drag marker) + if (isset($input['latitude']) && isset($input['longitude']) + && !isset($input['nama_masjid']) && !isset($input['radius_m'])) { + $latitude = (float)$input['latitude']; + $longitude = (float)$input['longitude']; + $stmt = $db->prepare("UPDATE data_masjid SET latitude = ?, longitude = ? WHERE id = ?"); + $stmt->bind_param('ddi', $latitude, $longitude, $id); + + // Update radius saja (dari slider tanpa simpan form) + } elseif (isset($input['radius_m']) && !isset($input['nama_masjid'])) { + $radius_m = max(100, min(5000, (int)$input['radius_m'])); + $stmt = $db->prepare("UPDATE data_masjid SET radius_m = ? WHERE id = ?"); + $stmt->bind_param('ii', $radius_m, $id); + + } else { + // Full update + $nama_masjid = trim($input['nama_masjid'] ?? ''); + $jenis = trim($input['jenis'] ?? 'Masjid'); + $pic_masjid = trim($input['pic_masjid'] ?? ''); + $alamat = trim($input['alamat'] ?? ''); + $radius_m = isset($input['radius_m']) ? max(100, min(5000, (int)$input['radius_m'])) : 500; + $latitude = (float)($input['latitude'] ?? 0); + $longitude = (float)($input['longitude'] ?? 0); + + $valid_jenis = ['Masjid','Gereja','Pura','Vihara','Klenteng']; + if (!in_array($jenis, $valid_jenis)) $jenis = 'Masjid'; + + if (empty($nama_masjid) || empty($pic_masjid) || empty($alamat)) { + http_response_code(400); + echo json_encode(['error' => 'nama_masjid, pic_masjid, dan alamat wajib diisi']); + exit(); + } + + $stmt = $db->prepare("UPDATE data_masjid SET nama_masjid = ?, jenis = ?, pic_masjid = ?, alamat = ?, radius_m = ?, latitude = ?, longitude = ? WHERE id = ?"); + $stmt->bind_param('sssiddi', $nama_masjid, $jenis, $pic_masjid, $alamat, $radius_m, $latitude, $longitude, $id); + } + + if ($stmt->execute()) { + echo json_encode(['message' => 'Data masjid berhasil diperbarui', 'id' => $id]); + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal memperbarui data: ' . $stmt->error]); + } + $db->close(); + exit(); +} + +// ---- DELETE: Hapus masjid ---- +if ($method === 'DELETE') { + if (!$id) { + http_response_code(400); + echo json_encode(['error' => 'ID diperlukan untuk hapus']); + exit(); + } + + $db = getDB(); + $stmt = $db->prepare("DELETE FROM data_masjid WHERE id = ?"); + $stmt->bind_param('i', $id); + + if ($stmt->execute()) { + if ($stmt->affected_rows > 0) { + echo json_encode(['message' => 'Data masjid berhasil dihapus', 'id' => $id]); + } else { + http_response_code(404); + echo json_encode(['error' => 'Data tidak ditemukan']); + } + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal menghapus data: ' . $stmt->error]); + } + $db->close(); + exit(); +} + +// Method tidak dikenal +http_response_code(405); +echo json_encode(['error' => 'Method tidak diizinkan']); +?> diff --git a/WebGIS_PovertyMap/api_parsil.php b/WebGIS_PovertyMap/api_parsil.php new file mode 100644 index 0000000..8d88a7a --- /dev/null +++ b/WebGIS_PovertyMap/api_parsil.php @@ -0,0 +1,169 @@ +prepare("SELECT id, no_parsil, pemilik, status, luas_m2, geojson FROM data_parsil WHERE id = ?"); + $stmt->bind_param('i', $id); + $stmt->execute(); + $result = $stmt->get_result()->fetch_assoc(); + if ($result) { + $result['geojson'] = json_decode($result['geojson']); + echo json_encode($result); + } else { + http_response_code(404); + echo json_encode(['error' => 'Data tidak ditemukan']); + } + } else { + // Get all + $result = $db->query("SELECT id, no_parsil, pemilik, status, luas_m2, geojson FROM data_parsil ORDER BY id DESC"); + $rows = []; + while ($row = $result->fetch_assoc()) { + $row['geojson'] = json_decode($row['geojson']); + $rows[] = $row; + } + echo json_encode($rows); + } + $db->close(); + exit(); +} + +// ---- POST: Tambah parsil tanah baru ---- +if ($method === 'POST') { + $input = json_decode(file_get_contents('php://input'), true); + + if (empty($input['no_parsil']) || empty($input['pemilik']) || empty($input['status']) || empty($input['geojson'])) { + http_response_code(400); + echo json_encode(['error' => 'Data tidak lengkap. Field wajib: no_parsil, pemilik, status, geojson']); + exit(); + } + + $no_parsil = trim($input['no_parsil']); + $pemilik = trim($input['pemilik']); + $status = $input['status']; + $luas_m2 = isset($input['luas_m2']) ? (float)$input['luas_m2'] : 0; + $geojson = json_encode($input['geojson']); + + // Validasi status + $valid_status = ['SHM', 'HGB', 'HGU', 'HP']; + if (!in_array($status, $valid_status)) { + http_response_code(400); + echo json_encode(['error' => 'Status tidak valid. Pilih: SHM, HGB, HGU, atau HP']); + exit(); + } + + $db = getDB(); + $stmt = $db->prepare("INSERT INTO data_parsil (no_parsil, pemilik, status, luas_m2, geojson) VALUES (?, ?, ?, ?, ?)"); + $stmt->bind_param('sssds', $no_parsil, $pemilik, $status, $luas_m2, $geojson); + + if ($stmt->execute()) { + $new_id = $db->insert_id; + http_response_code(201); + echo json_encode([ + 'message' => 'Data parsil berhasil disimpan', + 'id' => $new_id, + 'no_parsil' => $no_parsil, + 'pemilik' => $pemilik, + 'status' => $status, + 'luas_m2' => $luas_m2 + ]); + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal menyimpan data: ' . $stmt->error]); + } + $db->close(); + exit(); +} + +// ---- PUT: Update data parsil ---- +if ($method === 'PUT') { + if (!$id) { + http_response_code(400); + echo json_encode(['error' => 'ID diperlukan untuk update']); + exit(); + } + + $input = json_decode(file_get_contents('php://input'), true); + $db = getDB(); + + // Cek apakah hanya update geometri (edit bentuk di peta) + if (isset($input['geojson']) && !isset($input['no_parsil'])) { + // Update geometri & luas + $geojson = json_encode($input['geojson']); + $luas_m2 = isset($input['luas_m2']) ? (float)$input['luas_m2'] : 0; + $stmt = $db->prepare("UPDATE data_parsil SET geojson = ?, luas_m2 = ? WHERE id = ?"); + $stmt->bind_param('sdi', $geojson, $luas_m2, $id); + } else { + // Full update (edit semua atribut) + $no_parsil = trim($input['no_parsil'] ?? ''); + $pemilik = trim($input['pemilik'] ?? ''); + $status = $input['status'] ?? ''; + $luas_m2 = isset($input['luas_m2']) ? (float)$input['luas_m2'] : 0; + $geojson = json_encode($input['geojson'] ?? []); + + $valid_status = ['SHM', 'HGB', 'HGU', 'HP']; + if (!in_array($status, $valid_status)) { + http_response_code(400); + echo json_encode(['error' => 'Status tidak valid']); + exit(); + } + + $stmt = $db->prepare("UPDATE data_parsil SET no_parsil = ?, pemilik = ?, status = ?, luas_m2 = ?, geojson = ? WHERE id = ?"); + $stmt->bind_param('sssdsi', $no_parsil, $pemilik, $status, $luas_m2, $geojson, $id); + } + + if ($stmt->execute()) { + echo json_encode(['message' => 'Data parsil berhasil diperbarui', 'id' => $id]); + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal memperbarui data: ' . $stmt->error]); + } + $db->close(); + exit(); +} + +// ---- DELETE: Hapus parsil tanah ---- +if ($method === 'DELETE') { + if (!$id) { + http_response_code(400); + echo json_encode(['error' => 'ID diperlukan untuk hapus']); + exit(); + } + + $db = getDB(); + $stmt = $db->prepare("DELETE FROM data_parsil WHERE id = ?"); + $stmt->bind_param('i', $id); + + if ($stmt->execute()) { + if ($stmt->affected_rows > 0) { + echo json_encode(['message' => 'Data parsil berhasil dihapus', 'id' => $id]); + } else { + http_response_code(404); + echo json_encode(['error' => 'Data tidak ditemukan']); + } + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal menghapus data: ' . $stmt->error]); + } + $db->close(); + exit(); +} + +// Method tidak dikenal +http_response_code(405); +echo json_encode(['error' => 'Method tidak diizinkan']); +?> diff --git a/WebGIS_PovertyMap/backup_webgis_p3.sql b/WebGIS_PovertyMap/backup_webgis_p3.sql new file mode 100644 index 0000000..e69de29 diff --git a/WebGIS_PovertyMap/config.php b/WebGIS_PovertyMap/config.php new file mode 100644 index 0000000..ca3c330 --- /dev/null +++ b/WebGIS_PovertyMap/config.php @@ -0,0 +1,33 @@ +connect_error) { + http_response_code(500); + die(json_encode(['error' => 'Koneksi database gagal: ' . $conn->connect_error])); + } + $conn->set_charset('utf8mb4'); + return $conn; +} + +// Header CORS & JSON untuk semua response +header('Content-Type: application/json; charset=utf-8'); +header('Access-Control-Allow-Origin: *'); +header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); +header('Access-Control-Allow-Headers: Content-Type'); + +// Handle preflight OPTIONS request +if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { + http_response_code(200); + exit(); +} +?> diff --git a/WebGIS_PovertyMap/dashboard_walikota.html b/WebGIS_PovertyMap/dashboard_walikota.html new file mode 100644 index 0000000..d455494 --- /dev/null +++ b/WebGIS_PovertyMap/dashboard_walikota.html @@ -0,0 +1,474 @@ + + + + + + + + Dashboard Walikota – WebGIS Poverty Mapping + + + + + + + +
+
+ 👑 +

Dashboard Eksekutif

+ Walikota Pontianak +
+
+ + 🗺️ Lihat Peta + +
+
+ + +
+ +
+
+
🏚️
+
+
Total Warga Miskin
+
Memuat...
+
+
+
+
+
Sudah Dibantu
+
+
0%
+
+
+
+
+
Belum Dibantu
+
Memuat...
+
+
+
🕌
+
+
Rumah Ibadah Aktif
+
Memuat...
+
+
+
🎁
+
+
Total Bantuan Tercatat
+
Seluruh riwayat
+
+
+ + +
+ +
+
+

📊 Distribusi Kategori Kemiskinan

+ +
+
+
⏳ Memuat data...
+
+
+ + +
+
+

📢 Laporan Masyarakat Terbaru

+
+
+
⏳ Memuat data...
+
+
+
+ + +
+ +
+
+

🔥 Heatmap Distribusi Kemiskinan

+
+
+
+ + +
+
+

🏥 Riwayat Penyakit Terbanyak

+
+
+
⏳ Memuat data...
+
+
+
+
+ + + + + + + diff --git a/WebGIS_PovertyMap/database.sql b/WebGIS_PovertyMap/database.sql new file mode 100644 index 0000000..dca7763 --- /dev/null +++ b/WebGIS_PovertyMap/database.sql @@ -0,0 +1,88 @@ +-- ============================================================ +-- DATABASE: webgis_p3 +-- Pertemuan 3 – Layer Groups and Layers Control +-- Data SPBU dengan 2 Layer Group: Buka 24 Jam & Tidak 24 Jam +-- ============================================================ + +CREATE DATABASE IF NOT EXISTS webgis_p3 + CHARACTER SET utf8mb4 + COLLATE utf8mb4_unicode_ci; + +USE webgis_p3; + +-- ---- Tabel Data Jalan (Polyline) ---- +CREATE TABLE IF NOT EXISTS data_jalan ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama_jalan VARCHAR(200) NOT NULL, + status ENUM('Nasional','Provinsi','Kabupaten') NOT NULL DEFAULT 'Kabupaten', + panjang_m DOUBLE NOT NULL DEFAULT 0, + geojson LONGTEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ---- Tabel Data Parsil Tanah (Polygon) ---- +CREATE TABLE IF NOT EXISTS data_parsil ( + id INT AUTO_INCREMENT PRIMARY KEY, + no_parsil VARCHAR(100) NOT NULL, + pemilik VARCHAR(200) NOT NULL, + status ENUM('SHM','HGB','HGU','HP') NOT NULL DEFAULT 'SHM', + luas_m2 DOUBLE NOT NULL DEFAULT 0, + geojson LONGTEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ---- Tabel Data SPBU (Point/Marker) ---- +-- buka_24_jam: 'Ya' → Layer Group "SPBU 24 Jam" +-- 'Tidak' → Layer Group "SPBU Tidak 24 Jam" +CREATE TABLE IF NOT EXISTS data_spbu ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama_spbu VARCHAR(200) NOT NULL, + no_wa VARCHAR(20) NOT NULL, + buka_24_jam ENUM('Ya','Tidak') NOT NULL DEFAULT 'Tidak', + latitude DOUBLE NOT NULL, + longitude DOUBLE NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ---- Data Contoh SPBU (opsional, untuk testing) ---- +INSERT INTO data_spbu (nama_spbu, no_wa, buka_24_jam, latitude, longitude) VALUES +('SPBU Jl. Ahmad Yani', '0812-1111-0001', 'Ya', -0.0400, 109.3200), +('SPBU Jl. Gajah Mada', '0812-1111-0002', 'Ya', -0.0520, 109.3450), +('SPBU Jl. Diponegoro', '0812-1111-0003', 'Tidak', -0.0610, 109.3600), +('SPBU Jl. Sutan Syahrir', '0812-1111-0004', 'Tidak', -0.0480, 109.3700), +('SPBU Jl. Kom. Yos Sudarso', '0812-1111-0005', 'Ya', -0.0350, 109.3550); + +-- ============================================================ +-- Tabel Data Kemiskinan (Point/Marker) +-- ============================================================ +CREATE TABLE IF NOT EXISTS data_kemiskinan ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama_kk VARCHAR(200) NOT NULL, -- Nama kepala keluarga + jumlah_kk INT NOT NULL DEFAULT 1, -- Jumlah anggota dalam KK + alamat TEXT NOT NULL, + latitude DOUBLE NOT NULL, + longitude DOUBLE NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ============================================================ +-- Tabel Data Rumah Ibadah (Point/Marker + Radius Buffer) +-- jenis: Masjid, Gereja, Pura, Vihara, Klenteng +-- radius_m: radius lingkaran buffer dalam meter (100 – 5000) +-- ============================================================ +CREATE TABLE IF NOT EXISTS data_masjid ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama_masjid VARCHAR(200) NOT NULL, + jenis ENUM('Masjid','Gereja','Pura','Vihara','Klenteng') NOT NULL DEFAULT 'Masjid', + pic_masjid VARCHAR(200) NOT NULL, -- Person In Charge + alamat TEXT NOT NULL, + radius_m INT NOT NULL DEFAULT 500, -- Radius buffer (100-5000 m) + latitude DOUBLE NOT NULL, + longitude DOUBLE NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/WebGIS_PovertyMap/database_lengkap.sql b/WebGIS_PovertyMap/database_lengkap.sql new file mode 100644 index 0000000..5605dc0 --- /dev/null +++ b/WebGIS_PovertyMap/database_lengkap.sql @@ -0,0 +1,132 @@ +-- ============================================================ +-- DATABASE LENGKAP: webgis_p3 +-- WebGIS Poverty Mapping + Sistem Login (3 Role) +-- Import file ini ke Laragon phpMyAdmin +-- ============================================================ + +CREATE DATABASE IF NOT EXISTS webgis_p3 + CHARACTER SET utf8mb4 + COLLATE utf8mb4_unicode_ci; + +USE webgis_p3; + +-- ── Tabel Data Jalan (Polyline) ──────────────────────────── +CREATE TABLE IF NOT EXISTS data_jalan ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama_jalan VARCHAR(200) NOT NULL, + status ENUM('Nasional','Provinsi','Kabupaten') NOT NULL DEFAULT 'Kabupaten', + panjang_m DOUBLE NOT NULL DEFAULT 0, + geojson LONGTEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ── Tabel Data Parsil Tanah (Polygon) ───────────────────── +CREATE TABLE IF NOT EXISTS data_parsil ( + id INT AUTO_INCREMENT PRIMARY KEY, + no_parsil VARCHAR(100) NOT NULL, + pemilik VARCHAR(200) NOT NULL, + status ENUM('SHM','HGB','HGU','HP') NOT NULL DEFAULT 'SHM', + luas_m2 DOUBLE NOT NULL DEFAULT 0, + geojson LONGTEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ── Tabel Data SPBU (Point/Marker) ──────────────────────── +CREATE TABLE IF NOT EXISTS data_spbu ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama_spbu VARCHAR(200) NOT NULL, + no_wa VARCHAR(20) NOT NULL, + buka_24_jam ENUM('Ya','Tidak') NOT NULL DEFAULT 'Tidak', + latitude DOUBLE NOT NULL, + longitude DOUBLE NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ── Data Contoh SPBU ────────────────────────────────────── +INSERT INTO data_spbu (nama_spbu, no_wa, buka_24_jam, latitude, longitude) VALUES +('SPBU Jl. Ahmad Yani', '0812-1111-0001', 'Ya', -0.0400, 109.3200), +('SPBU Jl. Gajah Mada', '0812-1111-0002', 'Ya', -0.0520, 109.3450), +('SPBU Jl. Diponegoro', '0812-1111-0003', 'Tidak', -0.0610, 109.3600), +('SPBU Jl. Sutan Syahrir', '0812-1111-0004', 'Tidak', -0.0480, 109.3700), +('SPBU Jl. Kom. Yos Sudarso', '0812-1111-0005', 'Ya', -0.0350, 109.3550); + +-- ── Tabel Data Kemiskinan (Point/Marker) ────────────────── +CREATE TABLE IF NOT EXISTS data_kemiskinan ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama_kk VARCHAR(200) NOT NULL, + jumlah_kk INT NOT NULL DEFAULT 1, + alamat TEXT NOT NULL, + latitude DOUBLE NOT NULL, + longitude DOUBLE NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ── Tabel Data Rumah Ibadah (Point/Marker + Radius Buffer) +CREATE TABLE IF NOT EXISTS data_masjid ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama_masjid VARCHAR(200) NOT NULL, + jenis ENUM('Masjid','Mushola','Gereja','Pura','Vihara','Klenteng') NOT NULL DEFAULT 'Masjid', + pic_masjid VARCHAR(200) NOT NULL, + alamat TEXT NOT NULL, + radius_m INT NOT NULL DEFAULT 500, + latitude DOUBLE NOT NULL, + longitude DOUBLE NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ── Tabel Users (Sistem Login 3 Role) ───────────────────── +CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(100) NOT NULL UNIQUE, + nama_lengkap VARCHAR(200) NOT NULL, + role ENUM('admin','petugas','pengurus','walikota') NOT NULL, + password_hash VARCHAR(255) NOT NULL, + aktif TINYINT(1) NOT NULL DEFAULT 1, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + INDEX idx_role (role), + INDEX idx_aktif (aktif) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ── Akun Demo ───────────────────────────────────────────── +-- Password di-hash menggunakan bcrypt (PHP password_hash) +-- admin → admin123 +-- petugas → petugas123 +-- pengurus → pengurus123 +-- +-- Hash ini sudah valid, digenerate dengan PASSWORD_DEFAULT PHP 8 +INSERT INTO users (username, nama_lengkap, role, password_hash) VALUES +( + 'admin', + 'Administrator Sistem', + 'admin', + '$2y$12$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi' +), +( + 'petugas', + 'Petugas Lapangan', + 'petugas', + '$2y$12$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi' +), +( + 'pengurus', + 'Pengurus Rumah Ibadah', + 'pengurus', + '$2y$12$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi' +), +( + 'walikota', + 'Walikota Pontianak', + 'walikota', + '$2y$10$s0387VqXY8wBXYkVytEKxumpF0DmlBlE1z3usinr0nr1RYN/4BZw.' +); + +-- !! CATATAN: Hash di atas adalah placeholder "password". +-- Setelah import, jalankan generate_hash.php untuk meng-update +-- hash dengan password yang benar (admin123, petugas123, pengurus123). +-- URL: http://localhost/WebGIS/pertemuan fix/generate_hash.php diff --git a/WebGIS_PovertyMap/generate_hash.php b/WebGIS_PovertyMap/generate_hash.php new file mode 100644 index 0000000..7603f25 --- /dev/null +++ b/WebGIS_PovertyMap/generate_hash.php @@ -0,0 +1,94 @@ + 'admin', 'nama' => 'Administrator Sistem', 'role' => 'admin', 'password' => 'admin123'], + ['username' => 'pengurus', 'nama' => 'Pengurus Rumah Ibadah', 'role' => 'pengurus', 'password' => 'pengurus123'], + ['username' => 'walikota', 'nama' => 'Walikota Pontianak', 'role' => 'walikota', 'password' => 'walikota123'], +]; + +$db = getDB(); +$results = []; + +foreach ($demoUsers as $u) { + $hash = password_hash($u['password'], PASSWORD_DEFAULT); + + // INSERT or UPDATE + $stmt = $db->prepare( + "INSERT INTO users (username, nama_lengkap, role, password_hash) + VALUES (?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + nama_lengkap = VALUES(nama_lengkap), + role = VALUES(role), + password_hash = VALUES(password_hash)" + ); + $stmt->bind_param('ssss', $u['username'], $u['nama'], $u['role'], $hash); + + if ($stmt->execute()) { + $results[] = [ + 'status' => '✅ OK', + 'username' => $u['username'], + 'role' => $u['role'], + 'hash' => $hash + ]; + } else { + $results[] = [ + 'status' => '❌ ERROR: ' . $stmt->error, + 'username' => $u['username'], + 'role' => $u['role'], + 'hash' => '-' + ]; + } + $stmt->close(); +} + +$db->close(); + +// Output sebagai HTML yang mudah dibaca +header('Content-Type: text/html; charset=utf-8'); +?> + + + + +Generate Hash – WebGIS + + + +

🔑 Generate Hash – Akun Demo WebGIS

+ + + + + + + + + + + +
StatusUsernameRolePasswordHash (bcrypt)
+
+ ⚠️ PERHATIAN: File ini sudah tidak diperlukan. Hapus generate_hash.php setelah proses selesai untuk keamanan sistem. +
+

+ Setelah berhasil, buka login.html dan coba login. +

+ + diff --git a/WebGIS_PovertyMap/index.html b/WebGIS_PovertyMap/index.html new file mode 100644 index 0000000..707888c --- /dev/null +++ b/WebGIS_PovertyMap/index.html @@ -0,0 +1,2398 @@ + + + + + + + + Website Pemberantas Kemiskinan + + + + + + +
+
+ 🏚️ +

Website Pendata Kemiskinan

+
+
+
+
+ Sistem Online +
+ + + + + + + + +
+
+ +
+ +
+
+ + + + + + + +
+ Total Aktif: + Kemiskinan: 0 | + Rumah Ibadah: 0 +
+ + + +
+ + + + + + \ No newline at end of file diff --git a/WebGIS_PovertyMap/login.html b/WebGIS_PovertyMap/login.html new file mode 100644 index 0000000..05efdc2 --- /dev/null +++ b/WebGIS_PovertyMap/login.html @@ -0,0 +1,846 @@ + + + + + + + + Masuk – WebGIS Poverty Mapping + + + + + + + +
+
+
+ + +
+
+
+
+ + +
+ + + + + + +
+ +
+
+
+ Sistem Aktif +
+

+ Peta Kemiskinan
Berbasis Rumah Ibadah +

+

+ Platform pemetaan partisipatif yang menghubungkan data kemiskinan dengan jaringan rumah ibadah untuk + perencanaan program yang lebih tepat sasaran. +

+
+
+ 3 + Jenis Peran +
+
+
+ 6+ + Jenis Rumah Ibadah +
+
+
+ GIS + Berbasis Peta +
+
+
+
+ + +
+ +
+
🗺️
+
+ WebGIS Poverty Mapping + Sistem Informasi Geografis +
+
+ +

Masuk ke Sistem

+

Pilih peran, lalu masukkan kredensial Anda.

+ + +
+ + + +
+ + +
+ ⚠️ + Username atau password salah. +
+ + +
+
+ +
+ 👤 + +
+
+ +
+ +
+ 🔑 + + +
+
+ + +
+ +
+ + 🗺️ Lihat Peta & Lapor (Akses Warga) + +
+ + +
Akun Demo — Klik untuk Isi Otomatis
+
+ + + +
+ + +
+ + + + + \ No newline at end of file diff --git a/WebGIS_PovertyMap/migrasi_proses_bisnis.sql b/WebGIS_PovertyMap/migrasi_proses_bisnis.sql new file mode 100644 index 0000000..92cd098 --- /dev/null +++ b/WebGIS_PovertyMap/migrasi_proses_bisnis.sql @@ -0,0 +1,101 @@ +-- ============================================================ +-- MIGRASI: Penyesuaian Proses Bisnis WebGIS v2.0 +-- Jalankan di phpMyAdmin setelah database_lengkap.sql +-- ============================================================ + +USE webgis_p3; + +-- ── 1. Tambah kolom NIK, No WA, dan alasan_tolak ke laporan_masyarakat ── +-- Periksa apakah kolom sudah ada sebelum menambahkan + +-- Tambah kolom nik +SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = 'webgis_p3' AND TABLE_NAME = 'laporan_masyarakat' AND COLUMN_NAME = 'nik'); +SET @sql = IF(@col_exists = 0, + 'ALTER TABLE laporan_masyarakat ADD COLUMN nik VARCHAR(20) DEFAULT NULL AFTER nama_pelapor', + 'SELECT 1'); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- Tambah kolom no_wa +SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = 'webgis_p3' AND TABLE_NAME = 'laporan_masyarakat' AND COLUMN_NAME = 'no_wa'); +SET @sql = IF(@col_exists = 0, + 'ALTER TABLE laporan_masyarakat ADD COLUMN no_wa VARCHAR(20) DEFAULT NULL AFTER nik', + 'SELECT 1'); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- Tambah kolom alasan_tolak +SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = 'webgis_p3' AND TABLE_NAME = 'laporan_masyarakat' AND COLUMN_NAME = 'alasan_tolak'); +SET @sql = IF(@col_exists = 0, + 'ALTER TABLE laporan_masyarakat ADD COLUMN alasan_tolak TEXT DEFAULT NULL AFTER status', + 'SELECT 1'); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- ── 2. Perluas ENUM status di laporan_masyarakat ── +-- Tambah status: Diverifikasi, Ditolak +ALTER TABLE laporan_masyarakat + MODIFY COLUMN status ENUM('Pending','Diproses','Diverifikasi','Ditolak','Selesai') + NOT NULL DEFAULT 'Pending'; + +-- ── 3. Pastikan tabel riwayat_bantuan ada ── +CREATE TABLE IF NOT EXISTS riwayat_bantuan ( + id INT AUTO_INCREMENT PRIMARY KEY, + id_kemiskinan INT NOT NULL, + pemberi VARCHAR(200) NOT NULL, + jenis_bantuan VARCHAR(200) NOT NULL, + tanggal DATE NOT NULL, + keterangan TEXT DEFAULT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (id_kemiskinan) REFERENCES data_kemiskinan(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ── 4. Pastikan tabel laporan_masyarakat ada ── +CREATE TABLE IF NOT EXISTS laporan_masyarakat ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama_pelapor VARCHAR(200) NOT NULL, + nik VARCHAR(20) DEFAULT NULL, + no_wa VARCHAR(20) DEFAULT NULL, + deskripsi TEXT NOT NULL, + latitude DOUBLE NOT NULL, + longitude DOUBLE NOT NULL, + status ENUM('Pending','Diproses','Diverifikasi','Ditolak','Selesai') NOT NULL DEFAULT 'Pending', + alasan_tolak TEXT DEFAULT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ── 5. Tambahkan kolom yang mungkin belum ada di data_kemiskinan ── +-- status_bantuan +SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = 'webgis_p3' AND TABLE_NAME = 'data_kemiskinan' AND COLUMN_NAME = 'status_bantuan'); +SET @sql = IF(@col_exists = 0, + "ALTER TABLE data_kemiskinan ADD COLUMN status_bantuan VARCHAR(20) NOT NULL DEFAULT 'Belum' AFTER longitude", + 'SELECT 1'); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- kategori +SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = 'webgis_p3' AND TABLE_NAME = 'data_kemiskinan' AND COLUMN_NAME = 'kategori'); +SET @sql = IF(@col_exists = 0, + "ALTER TABLE data_kemiskinan ADD COLUMN kategori VARCHAR(50) NOT NULL DEFAULT 'Miskin' AFTER status_bantuan", + 'SELECT 1'); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- ── 6. Update password walikota (gunakan generate_hash.php untuk update bcrypt) ── +-- Password walikota: walikota123 +-- Jalankan generate_hash.php setelah migrasi ini untuk update hash + +-- ============================================================ +-- SELESAI. Jalankan generate_hash.php untuk update password hash. +-- URL: http://localhost/WebGIS/pertemuan fix/generate_hash.php +-- ============================================================ diff --git a/WebGIS_PovertyMap/migrate_lapor.php b/WebGIS_PovertyMap/migrate_lapor.php new file mode 100644 index 0000000..3df8a91 --- /dev/null +++ b/WebGIS_PovertyMap/migrate_lapor.php @@ -0,0 +1,28 @@ +query($q)) { + echo "Success: $q\n
"; + } else { + echo "Error/Skipped (might already exist): " . $db->error . " | Query: $q\n
"; + } + } catch (Exception $e) { + echo "Exception/Skipped: " . $e->getMessage() . " | Query: $q\n
"; + } +} +$db->close(); +echo "Migration complete.\n"; +?> diff --git a/WebGIS_PovertyMap/migrate_lapor2.php b/WebGIS_PovertyMap/migrate_lapor2.php new file mode 100644 index 0000000..2ecc32f --- /dev/null +++ b/WebGIS_PovertyMap/migrate_lapor2.php @@ -0,0 +1,29 @@ +query($q)) { + echo "Success: $q\n
"; + } else { + echo "Error/Skipped: " . $db->error . " | Query: $q\n
"; + } + } catch (\Throwable $e) { + echo "Exception/Skipped: " . $e->getMessage() . " | Query: $q\n
"; + } +} +$db->close(); +echo "Migration complete.\n"; +?> diff --git a/WebGIS_PovertyMap/test_db.php b/WebGIS_PovertyMap/test_db.php new file mode 100644 index 0000000..8e2c3b0 --- /dev/null +++ b/WebGIS_PovertyMap/test_db.php @@ -0,0 +1,11 @@ +connect_error ? "GAGAL - ".$c1->connect_error : "OK") . "
"; + +// Test 2: 127.0.0.1 +$c2 = @new mysqli('127.0.0.1', 'root', '', 'webgis_p3'); +echo "127.0.0.1: " . ($c2->connect_error ? "GAGAL - ".$c2->connect_error : "OK") . "
"; + +phpinfo(); +?> diff --git a/WebGIS_PovertyMap/users_migration.sql b/WebGIS_PovertyMap/users_migration.sql new file mode 100644 index 0000000..29e8930 --- /dev/null +++ b/WebGIS_PovertyMap/users_migration.sql @@ -0,0 +1,89 @@ +-- ============================================================ +-- MIGRASI: Tabel Users & Role-Based Access Control +-- WebGIS Poverty Mapping – Sistem Autentikasi +-- Jalankan di phpMyAdmin atau MySQL CLI setelah database.sql +-- ============================================================ + +USE webgis_p3; + +-- ── Tabel pengguna ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(100) NOT NULL UNIQUE, + nama_lengkap VARCHAR(200) NOT NULL, + role ENUM('admin','petugas','pengurus','walikota') NOT NULL, + password_hash VARCHAR(255) NOT NULL, + aktif TINYINT(1) NOT NULL DEFAULT 1, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + INDEX idx_role (role), + INDEX idx_aktif (aktif) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ── Akun demo (password di-hash dengan bcrypt PHP PASSWORD_DEFAULT) ── +-- Gunakan script generate_hash.php untuk membuat hash baru jika perlu. +-- +-- admin → admin123 +-- petugas → petugas123 +-- pengurus → pengurus123 +-- +-- Hash di bawah digenerate dengan PHP: +-- password_hash('admin123', PASSWORD_DEFAULT) → $2y$12$... +-- password_hash('petugas123', PASSWORD_DEFAULT) → $2y$12$... +-- password_hash('pengurus123', PASSWORD_DEFAULT) → $2y$12$... +-- +-- !! Jangan gunakan plain text password di produksi !! + +INSERT INTO users (username, nama_lengkap, role, password_hash) VALUES +( + 'admin', + 'Administrator Sistem', + 'admin', + '$2y$12$YourHashHereReplaceMe.admin123HashGeneratedByPHP' +), +( + 'petugas', + 'Petugas Lapangan', + 'petugas', + '$2y$12$YourHashHereReplaceMe.petugas123HashGeneratedByPHP' +), +( + 'pengurus', + 'Pengurus Rumah Ibadah', + 'pengurus', + '$2y$12$YourHashHereReplaceMe.pengurus123HashGeneratedByPHP' +), +( + 'walikota', + 'Walikota Pontianak', + 'walikota', + '$2y$10$s0387VqXY8wBXYkVytEKxumpF0DmlBlE1z3usinr0nr1RYN/4BZw.' +); + +-- !! PENTING: Setelah import tabel di atas, jalankan generate_hash.php +-- untuk mengisi password_hash yang benar secara otomatis. !! + +-- ── Catatan hak akses per role ────────────────────────────── +/* + ROLE: admin + ───────────────────────────────────────────────────────────── + • CRUD semua data: SPBU, kemiskinan, rumah ibadah + • Dapat melihat semua layer & analisis spasial + • Fitur FAB Tambah: SPBU, Kemiskinan, Rumah Ibadah + + ROLE: petugas + ───────────────────────────────────────────────────────────── + • Input & edit data kemiskinan (CREATE + UPDATE + DELETE) + • View semua data (read-only untuk SPBU & Rumah Ibadah) + • Fitur FAB Tambah: hanya Kemiskinan + • Tidak dapat hapus data SPBU atau Rumah Ibadah + • Tidak dapat tambah/edit SPBU atau Rumah Ibadah + + ROLE: pengurus + ───────────────────────────────────────────────────────────── + • Input & edit data rumah ibadah miliknya (CREATE + UPDATE + DELETE) + • View data kemiskinan (read-only) + • Fitur FAB Tambah: hanya Rumah Ibadah + • Tidak dapat hapus data SPBU atau kemiskinan + • Tidak dapat tambah/edit SPBU atau kemiskinan +*/ diff --git a/WebGIS_SPBU/api_spbu.php b/WebGIS_SPBU/api_spbu.php new file mode 100644 index 0000000..9544aa4 --- /dev/null +++ b/WebGIS_SPBU/api_spbu.php @@ -0,0 +1,197 @@ +prepare( + "SELECT id, nama_spbu, alamat, no_wa, buka_24_jam, latitude, longitude + FROM data_spbu WHERE id = ?" + ); + $stmt->bind_param('i', $id); + $stmt->execute(); + $result = $stmt->get_result()->fetch_assoc(); + if ($result) { + $result['latitude'] = (float)$result['latitude']; + $result['longitude'] = (float)$result['longitude']; + echo json_encode($result); + } else { + http_response_code(404); + echo json_encode(['error' => 'Data SPBU tidak ditemukan']); + } + } else { + $result = $db->query( + "SELECT id, nama_spbu, alamat, no_wa, buka_24_jam, latitude, longitude + FROM data_spbu ORDER BY id ASC" + ); + if (!$result) { + jsonError('Query gagal: ' . $db->error); + } + $rows = []; + while ($row = $result->fetch_assoc()) { + $row['latitude'] = (float)$row['latitude']; + $row['longitude'] = (float)$row['longitude']; + $rows[] = $row; + } + echo json_encode($rows, JSON_UNESCAPED_UNICODE); + } + $db->close(); + exit(); +} + +if ($method === 'POST') { + $input = $jsonInput; + + if (empty($input['nama_spbu']) || empty($input['alamat']) || empty($input['no_wa']) + || !isset($input['latitude']) || !isset($input['longitude'])) { + http_response_code(400); + echo json_encode(['error' => 'Data tidak lengkap. Field wajib: nama_spbu, alamat, no_wa, latitude, longitude']); + exit(); + } + + $nama_spbu = trim($input['nama_spbu']); + $alamat = trim($input['alamat']); + $no_wa = trim($input['no_wa']); + $buka_24_jam = isset($input['buka_24_jam']) && $input['buka_24_jam'] === 'Ya' ? 'Ya' : 'Tidak'; + $latitude = (float)$input['latitude']; + $longitude = (float)$input['longitude']; + + $db = getDB(); + ensureSpbuSchema($db); + $stmt = $db->prepare( + "INSERT INTO data_spbu (nama_spbu, alamat, no_wa, buka_24_jam, latitude, longitude) + VALUES (?, ?, ?, ?, ?, ?)" + ); + $stmt->bind_param('ssssdd', $nama_spbu, $alamat, $no_wa, $buka_24_jam, $latitude, $longitude); + + if ($stmt->execute()) { + http_response_code(201); + echo json_encode([ + 'message' => 'Data SPBU berhasil disimpan', + 'id' => $db->insert_id, + 'nama_spbu' => $nama_spbu, + 'alamat' => $alamat, + 'no_wa' => $no_wa, + 'buka_24_jam' => $buka_24_jam, + 'latitude' => $latitude, + 'longitude' => $longitude + ]); + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal menyimpan data: ' . $stmt->error]); + } + $db->close(); + exit(); +} + +if ($method === 'PUT') { + if (!$id) { + http_response_code(400); + echo json_encode(['error' => 'ID diperlukan untuk update']); + exit(); + } + + $input = $jsonInput; + $db = getDB(); + ensureSpbuSchema($db); + + if (isset($input['latitude']) && isset($input['longitude']) + && !isset($input['nama_spbu'])) { + $latitude = (float)$input['latitude']; + $longitude = (float)$input['longitude']; + $stmt = $db->prepare("UPDATE data_spbu SET latitude = ?, longitude = ? WHERE id = ?"); + $stmt->bind_param('ddi', $latitude, $longitude, $id); + } else { + $nama_spbu = trim($input['nama_spbu'] ?? ''); + $alamat = trim($input['alamat'] ?? ''); + $no_wa = trim($input['no_wa'] ?? ''); + $buka_24_jam = isset($input['buka_24_jam']) && $input['buka_24_jam'] === 'Ya' ? 'Ya' : 'Tidak'; + $latitude = (float)($input['latitude'] ?? 0); + $longitude = (float)($input['longitude'] ?? 0); + + if (empty($nama_spbu) || empty($alamat) || empty($no_wa)) { + http_response_code(400); + echo json_encode(['error' => 'nama_spbu, alamat, dan no_wa wajib diisi']); + exit(); + } + + $stmt = $db->prepare( + "UPDATE data_spbu + SET nama_spbu = ?, alamat = ?, no_wa = ?, buka_24_jam = ?, latitude = ?, longitude = ? + WHERE id = ?" + ); + $stmt->bind_param('ssssddi', $nama_spbu, $alamat, $no_wa, $buka_24_jam, $latitude, $longitude, $id); + } + + if ($stmt->execute()) { + echo json_encode(['message' => 'Data SPBU berhasil diperbarui', 'id' => $id]); + } else { + http_response_code(500); + echo json_encode(['error' => 'Gagal memperbarui data: ' . $stmt->error]); + } + $db->close(); + exit(); +} + +if ($method === 'DELETE') { + if (!$id) { + jsonError('ID diperlukan untuk hapus', 400); + } + + $db = getDB(); + ensureSpbuSchema($db); + $stmt = $db->prepare("DELETE FROM data_spbu WHERE id = ?"); + if (!$stmt) { + jsonError('Gagal menyiapkan query hapus: ' . $db->error); + } + + $stmt->bind_param('i', $id); + + if ($stmt->execute()) { + if ($stmt->affected_rows > 0) { + echo json_encode(['message' => 'Data SPBU berhasil dihapus', 'id' => $id], JSON_UNESCAPED_UNICODE); + } else { + jsonError('Data SPBU tidak ditemukan', 404); + } + } else { + jsonError('Gagal menghapus data: ' . $stmt->error); + } + $stmt->close(); + $db->close(); + exit(); +} + +jsonError('Method tidak diizinkan', 405); + +} catch (Throwable $e) { + jsonError('Server error: ' . $e->getMessage()); +} diff --git a/WebGIS_SPBU/config.php b/WebGIS_SPBU/config.php new file mode 100644 index 0000000..68ea3f5 --- /dev/null +++ b/WebGIS_SPBU/config.php @@ -0,0 +1,103 @@ + $message]); + exit(); +} + +function getDB() { + mysqli_report(MYSQLI_REPORT_OFF); + + $conn = new mysqli(DB_HOST, DB_USER, DB_PASS); + if ($conn->connect_error) { + http_response_code(500); + die(json_encode(['error' => 'Koneksi MySQL gagal: ' . $conn->connect_error])); + } + + $dbName = DB_NAME; + if (!$conn->query("CREATE DATABASE IF NOT EXISTS `{$dbName}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci")) { + http_response_code(500); + die(json_encode(['error' => 'Gagal membuat database: ' . $conn->error])); + } + + if (!$conn->select_db($dbName)) { + http_response_code(500); + die(json_encode(['error' => 'Gagal memilih database: ' . $conn->error])); + } + + $conn->set_charset('utf8mb4'); + return $conn; +} + +function ensureSpbuSchema($db) { + if (!$db->query(" + CREATE TABLE IF NOT EXISTS data_spbu ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama_spbu VARCHAR(200) NOT NULL, + alamat VARCHAR(300) NOT NULL DEFAULT '', + no_wa VARCHAR(20) NOT NULL, + buka_24_jam ENUM('Ya','Tidak') NOT NULL DEFAULT 'Tidak', + latitude DOUBLE NOT NULL, + longitude DOUBLE NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 + ")) { + jsonError('Gagal membuat tabel data_spbu: ' . $db->error); + } + + $check = $db->query("SHOW COLUMNS FROM data_spbu LIKE 'alamat'"); + if ($check && $check->num_rows === 0) { + $db->query("ALTER TABLE data_spbu ADD COLUMN alamat VARCHAR(300) NOT NULL DEFAULT '' AFTER nama_spbu"); + $db->query("UPDATE data_spbu SET alamat = CONCAT(nama_spbu, ', Pontianak') WHERE alamat = ''"); + } + + $countRes = $db->query("SELECT COUNT(*) AS total FROM data_spbu"); + if ($countRes && (int)$countRes->fetch_assoc()['total'] === 0) { + seedSpbuSampleData($db); + } +} + +function seedSpbuSampleData($db) { + $samples = [ + ['SPBU Jl. Ahmad Yani', 'Jl. Ahmad Yani No. 12, Pontianak', '0812-1111-0001', 'Ya', -0.0400, 109.3200], + ['SPBU Jl. Gajah Mada', 'Jl. Gajah Mada No. 45, Pontianak', '0812-1111-0002', 'Ya', -0.0520, 109.3450], + ['SPBU Jl. Diponegoro', 'Jl. Diponegoro No. 88, Pontianak', '0812-1111-0003', 'Tidak', -0.0610, 109.3600], + ['SPBU Jl. Sutan Syahrir', 'Jl. Sutan Syahrir No. 5, Pontianak', '0812-1111-0004', 'Tidak', -0.0480, 109.3700], + ['SPBU Jl. Kom. Yos Sudarso', 'Jl. Kom. Yos Sudarso No. 22, Pontianak', '0812-1111-0005', 'Ya', -0.0350, 109.3550], + ['SPBU Jl. Tanjung Raya', 'Jl. Tanjung Raya No. 101, Pontianak', '0812-1111-0006', 'Tidak', -0.0580, 109.3300], + ['SPBU Jl. Veteran', 'Jl. Veteran No. 33, Pontianak', '0812-1111-0007', 'Ya', -0.0450, 109.3380], + ]; + + $stmt = $db->prepare( + "INSERT INTO data_spbu (nama_spbu, alamat, no_wa, buka_24_jam, latitude, longitude) + VALUES (?, ?, ?, ?, ?, ?)" + ); + + foreach ($samples as $row) { + [$nama, $alamat, $wa, $jam, $lat, $lng] = $row; + $stmt->bind_param('ssssdd', $nama, $alamat, $wa, $jam, $lat, $lng); + $stmt->execute(); + } + $stmt->close(); +} + +header('Content-Type: application/json; charset=utf-8'); +header('Access-Control-Allow-Origin: *'); +header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); +header('Access-Control-Allow-Headers: Content-Type'); + +if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { + http_response_code(200); + exit(); +} diff --git a/WebGIS_SPBU/database.sql b/WebGIS_SPBU/database.sql new file mode 100644 index 0000000..033ea89 --- /dev/null +++ b/WebGIS_SPBU/database.sql @@ -0,0 +1,38 @@ +-- ============================================================ +-- DATABASE: webgis_spbu +-- Pertemuan 3 – Layer Groups and Layers Control +-- Data SPBU dengan 2 Layer Group: Buka 24 Jam & Tidak 24 Jam +-- ============================================================ + +CREATE DATABASE IF NOT EXISTS webgis_spbu + CHARACTER SET utf8mb4 + COLLATE utf8mb4_unicode_ci; + +USE webgis_spbu; + +-- buka_24_jam: 'Ya' → Layer Group "SPBU Buka 24 Jam" +-- 'Tidak' → Layer Group "SPBU Tidak Buka 24 Jam" +CREATE TABLE IF NOT EXISTS data_spbu ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama_spbu VARCHAR(200) NOT NULL, + alamat VARCHAR(300) NOT NULL, + no_wa VARCHAR(20) NOT NULL, + buka_24_jam ENUM('Ya','Tidak') NOT NULL DEFAULT 'Tidak', + latitude DOUBLE NOT NULL, + longitude DOUBLE NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- Migrasi: tambah kolom alamat jika tabel lama dari PovertyMap belum punya +-- ALTER TABLE data_spbu ADD COLUMN alamat VARCHAR(300) NOT NULL DEFAULT '' AFTER nama_spbu; +-- UPDATE data_spbu SET alamat = CONCAT(nama_spbu, ', Pontianak') WHERE alamat = ''; + +INSERT IGNORE INTO data_spbu (nama_spbu, alamat, no_wa, buka_24_jam, latitude, longitude) VALUES +('SPBU Jl. Ahmad Yani', 'Jl. Ahmad Yani No. 12, Pontianak', '0812-1111-0001', 'Ya', -0.0400, 109.3200), +('SPBU Jl. Gajah Mada', 'Jl. Gajah Mada No. 45, Pontianak', '0812-1111-0002', 'Ya', -0.0520, 109.3450), +('SPBU Jl. Diponegoro', 'Jl. Diponegoro No. 88, Pontianak', '0812-1111-0003', 'Tidak', -0.0610, 109.3600), +('SPBU Jl. Sutan Syahrir', 'Jl. Sutan Syahrir No. 5, Pontianak', '0812-1111-0004', 'Tidak', -0.0480, 109.3700), +('SPBU Jl. Kom. Yos Sudarso', 'Jl. Kom. Yos Sudarso No. 22, Pontianak', '0812-1111-0005', 'Ya', -0.0350, 109.3550), +('SPBU Jl. Tanjung Raya', 'Jl. Tanjung Raya No. 101, Pontianak', '0812-1111-0006', 'Tidak', -0.0580, 109.3300), +('SPBU Jl. Veteran', 'Jl. Veteran No. 33, Pontianak', '0812-1111-0007', 'Ya', -0.0450, 109.3380); diff --git a/WebGIS_SPBU/index.html b/WebGIS_SPBU/index.html new file mode 100644 index 0000000..5e38590 --- /dev/null +++ b/WebGIS_SPBU/index.html @@ -0,0 +1,562 @@ + + + + + + + + WebGIS SPBU – Layer Groups + + + + + + +
+
+

⛽ WebGIS SPBU Pontianak

+

Layer Groups & Layers Control – Pertemuan 2

+
+
+ 🟢 24 Jam: 0 + 🔴 Tidak 24 Jam: 0 +
+
+ +
+ +
+ +
+ +
+ + + + + + \ No newline at end of file diff --git a/WebGIS_SPBU/style.css b/WebGIS_SPBU/style.css new file mode 100644 index 0000000..1d49d84 --- /dev/null +++ b/WebGIS_SPBU/style.css @@ -0,0 +1,293 @@ +:root { + --bg-dark: #0f1117; + --border: #2a3248; + --accent-green: #10b981; + --accent-orange: #f97316; + --accent-blue: #3b82f6; + --accent-yellow: #f59e0b; + --accent-red: #ef4444; + --text-primary: #f1f5f9; + --text-secondary: #94a3b8; + --text-muted: #64748b; + --radius-sm: 6px; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Inter', system-ui, sans-serif; + background: var(--bg-dark); + color: var(--text-primary); + height: 100vh; + display: flex; + flex-direction: column; + overflow: hidden; +} + +#header-bar { + background: linear-gradient(135deg, #161b27 0%, #0f1117 100%); + border-bottom: 1px solid var(--border); + padding: 14px 20px; + display: flex; + justify-content: space-between; + align-items: center; + z-index: 1000; +} + +#header-bar h1 { + font-size: 18px; + font-weight: 700; +} + +#header-bar p { + font-size: 12px; + color: var(--text-secondary); + margin-top: 2px; +} + +#stats-bar { + display: flex; + gap: 12px; + font-size: 12px; +} + +.stat-badge { + padding: 6px 12px; + border-radius: 20px; + font-weight: 600; + border: 1px solid var(--border); + background: rgba(255, 255, 255, 0.04); +} + +.stat-badge.green { + color: var(--accent-green); + border-color: rgba(16, 185, 129, 0.35); +} + +.stat-badge.orange { + color: var(--accent-orange); + border-color: rgba(249, 115, 22, 0.35); +} + +#map { + flex: 1; + width: 100%; +} + +.leaflet-control-layers { + border-radius: 8px !important; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.35) !important; + font-size: 13px; +} + +.leaflet-control-layers-expanded { + padding: 10px 12px !important; +} + +.gis-popup { + min-width: 220px; + font-family: 'Inter', system-ui, sans-serif; +} + +.popup-title { + font-size: 14px; + font-weight: 700; + margin-bottom: 10px; + padding-bottom: 8px; + border-bottom: 2px solid var(--border); +} + +.info-row { + display: flex; + justify-content: space-between; + gap: 12px; + margin-bottom: 6px; + font-size: 12px; +} + +.info-label { + color: var(--text-muted); + flex-shrink: 0; +} + +.info-value { + text-align: right; + font-weight: 500; +} + +.status-badge { + display: inline-block; + padding: 2px 8px; + border-radius: 4px; + font-size: 11px; + font-weight: 700; +} + +.status-24jam { + background: rgba(16, 185, 129, 0.15); + color: #10b981; +} + +.status-tidak24jam { + background: rgba(249, 115, 22, 0.15); + color: #f97316; +} + +#toast { + position: fixed; + bottom: 24px; + left: 50%; + transform: translateX(-50%) translateY(80px); + background: #1e2538; + color: var(--text-primary); + padding: 10px 20px; + border-radius: 8px; + border: 1px solid var(--border); + font-size: 13px; + z-index: 9999; + opacity: 0; + transition: all 0.3s ease; + pointer-events: none; +} + +#toast.show { + opacity: 1; + transform: translateX(-50%) translateY(0); +} + +#toast.error { + border-color: rgba(239, 68, 68, 0.5); + color: #fca5a5; +} + +#toast.success { + border-color: rgba(16, 185, 129, 0.5); + color: #6ee7b7; +} + +#toast.info { + border-color: rgba(59, 130, 246, 0.5); + color: #93c5fd; +} + +.fab-container { + position: fixed; + bottom: 30px; + right: 30px; + z-index: 9999; +} + +.fab-btn { + width: 56px; + height: 56px; + border-radius: 50%; + background: linear-gradient(135deg, var(--accent-green), #059669); + color: white; + border: none; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4); + font-size: 24px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s; +} + +.fab-btn:hover { + transform: scale(1.05); +} + +.fab-btn.active { + background: linear-gradient(135deg, var(--accent-orange), #ea580c); + box-shadow: 0 0 0 4px rgba(249, 115, 22, 0.3); +} + +.popup-form label { + display: block; + font-size: 10px; + font-weight: 600; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 4px; + margin-top: 10px; +} + +.popup-form label:first-of-type { + margin-top: 0; +} + +.popup-form input, +.popup-form select { + width: 100%; + padding: 8px 10px; + background: var(--bg-dark); + border: 1px solid var(--border); + border-radius: var(--radius-sm); + color: var(--text-primary); + font-size: 12px; + font-family: inherit; + outline: none; +} + +.popup-form input:focus, +.popup-form select:focus { + border-color: var(--accent-blue); +} + +.popup-actions { + display: flex; + gap: 6px; + margin-top: 12px; +} + +.popup-btn { + flex: 1; + padding: 8px; + border: none; + border-radius: var(--radius-sm); + font-size: 11px; + font-weight: 600; + font-family: inherit; + cursor: pointer; + transition: opacity 0.2s; + pointer-events: auto; + position: relative; + z-index: 10; +} + +.popup-btn:hover { + opacity: 0.85; +} + +.popup-btn-save { + background: var(--accent-green); + color: #fff; +} + +.popup-btn-edit { + background: var(--accent-yellow); + color: #000; +} + +.popup-btn-delete { + background: var(--accent-red); + color: #fff; +} + +.popup-btn-cancel { + background: #334155; + color: #fff; +} + +.leaflet-popup-content-wrapper { + border-radius: 10px; +} + +.leaflet-popup-content { + margin: 12px 14px; +}