first commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<base target="_top">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>Quick Start - Leaflet</title>
|
||||
|
||||
<link rel="shortcut icon" type="image/x-icon" href="docs/images/favicon.ico" />
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
.leaflet-container {
|
||||
height: 400px;
|
||||
width: 600px;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
<div id="map" style="width: 2000px; height: 1000px;"></div>
|
||||
<script>
|
||||
|
||||
const map = L.map('map').setView([-0.056205, 109.348733], 15);
|
||||
|
||||
const tiles = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||
}).addTo(map);
|
||||
|
||||
const marker = L.marker([-0.056201, 109.348731]).addTo(map)
|
||||
.bindPopup('<b>Hello world!</b><br />I am a popup.').openPopup();
|
||||
|
||||
const circle = L.circle([-0.055691, 109.349946], {
|
||||
color: 'red',
|
||||
fillColor: '#f03',
|
||||
fillOpacity: 0.5,
|
||||
radius: 500
|
||||
}).addTo(map).bindPopup('I am a circle.');
|
||||
|
||||
const polygon = L.polygon([
|
||||
[-0.05708506770569824, 109.34910185041508],
|
||||
[-0.056110709503542314, 109.34985968494183],
|
||||
[-0.05727087874628323, 109.35014341690622]
|
||||
]).addTo(map).bindPopup('I am a polygon.');
|
||||
|
||||
|
||||
const popup = L.popup()
|
||||
.setLatLng([-0.056207, 109.348737])
|
||||
.setContent('I am a standalone popup.')
|
||||
.openOn(map);
|
||||
|
||||
function onMapClick(e) {
|
||||
popup
|
||||
.setLatLng(e.latlng)
|
||||
.setContent(`You clicked the map at ${e.latlng.toString()}`)
|
||||
.openOn(map);
|
||||
}
|
||||
|
||||
map.on('click', onMapClick);
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,155 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<base target="_top">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>Choropleth Tutorial - Leaflet</title>
|
||||
|
||||
<link rel="shortcut icon" type="image/x-icon" href="docs/images/favicon.ico" />
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
.leaflet-container {
|
||||
height: 400px;
|
||||
width: 600px;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>#map { width: 2000px; height: 1000px; }
|
||||
.info { padding: 6px 8px; font: 14px/16px Arial, Helvetica, sans-serif; background: white; background: rgba(255,255,255,0.8); box-shadow: 0 0 15px rgba(0,0,0,0.2); border-radius: 5px; } .info h4 { margin: 0 0 5px; color: #777; }
|
||||
.legend { text-align: left; line-height: 18px; color: #555; } .legend i { width: 18px; height: 18px; float: left; margin-right: 8px; opacity: 0.7; }</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id='map'></div>
|
||||
|
||||
<script type="text/javascript" src="Admin_Kecamatan_WGS84.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
const map = L.map('map').setView([-0.056205, 109.348733], 12);
|
||||
|
||||
const tiles = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||
}).addTo(map);
|
||||
|
||||
// control that shows state info on hover
|
||||
const info = L.control();
|
||||
|
||||
info.onAdd = function (map) {
|
||||
this._div = L.DomUtil.create('div', 'info');
|
||||
this.update();
|
||||
return this._div;
|
||||
};
|
||||
|
||||
info.update = function (props) {
|
||||
const contents = props ? `<b>${props.kecamatan}</b><br />${props.jumlah_penduduk.toLocaleString()} penduduk` : 'Hover over a kecamatan';
|
||||
this._div.innerHTML = `<h4>Data Kecamatan</h4>${contents}`;
|
||||
};
|
||||
|
||||
info.addTo(map);
|
||||
|
||||
|
||||
// get color depending on population density value
|
||||
function getColor(d) {
|
||||
return d > 1000 ? '#800026' :
|
||||
d > 500 ? '#BD0026' :
|
||||
d > 200 ? '#E31A1C' :
|
||||
d > 100 ? '#FC4E2A' :
|
||||
d > 50 ? '#FD8D3C' :
|
||||
d > 20 ? '#FEB24C' :
|
||||
d > 10 ? '#FED976' : '#FFEDA0';
|
||||
}
|
||||
|
||||
function style(feature) {
|
||||
return {
|
||||
weight: 2,
|
||||
opacity: 1,
|
||||
color: 'white',
|
||||
dashArray: '3',
|
||||
fillOpacity: 0.7,
|
||||
fillColor: getColor(feature.properties.density)
|
||||
};
|
||||
}
|
||||
|
||||
function highlightFeature(e) {
|
||||
const layer = e.target;
|
||||
|
||||
layer.setStyle({
|
||||
weight: 5,
|
||||
color: '#666',
|
||||
dashArray: '',
|
||||
fillOpacity: 0.7
|
||||
});
|
||||
|
||||
layer.bringToFront();
|
||||
|
||||
info.update(layer.feature.properties);
|
||||
}
|
||||
|
||||
/* global kecamatanData */
|
||||
const geojson = L.geoJson(kecamatanData, {
|
||||
style,
|
||||
onEachFeature
|
||||
}).addTo(map);
|
||||
|
||||
function resetHighlight(e) {
|
||||
geojson.resetStyle(e.target);
|
||||
info.update();
|
||||
}
|
||||
|
||||
function zoomToFeature(e) {
|
||||
map.fitBounds(e.target.getBounds());
|
||||
}
|
||||
|
||||
function onEachFeature(feature, layer) {
|
||||
layer.on({
|
||||
mouseover: highlightFeature,
|
||||
mouseout: resetHighlight,
|
||||
click: zoomToFeature
|
||||
});
|
||||
}
|
||||
|
||||
map.attributionControl.addAttribution('Population data © <a href="http://census.gov/">US Census Bureau</a>');
|
||||
|
||||
|
||||
const legend = L.control({position: 'bottomright'});
|
||||
|
||||
legend.onAdd = function (map) {
|
||||
|
||||
const div = L.DomUtil.create('div', 'info legend');
|
||||
const grades = [0, 10, 20, 50, 100, 200, 500, 1000];
|
||||
const labels = [];
|
||||
let from, to;
|
||||
|
||||
for (let i = 0; i < grades.length; i++) {
|
||||
from = grades[i];
|
||||
to = grades[i + 1];
|
||||
|
||||
labels.push(`<i style="background:${getColor(from + 1)}"></i> ${from}${to ? `–${to}` : '+'}`);
|
||||
}
|
||||
|
||||
div.innerHTML = labels.join('<br>');
|
||||
return div;
|
||||
};
|
||||
|
||||
legend.addTo(map);
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
// ============================================================
|
||||
// config.php — Koneksi ke MySQL
|
||||
// Ganti nilai di bawah sesuai pengaturan XAMPP / phpMyAdmin
|
||||
// ============================================================
|
||||
|
||||
define('DB_HOST', 'localhost'); // biasanya localhost
|
||||
define('DB_NAME', 'gis_ptk'); // nama database (buat dulu di phpMyAdmin)
|
||||
define('DB_USER', 'root'); // username MySQL (default XAMPP: root)
|
||||
define('DB_PASS', ''); // password MySQL (default XAMPP: kosong)
|
||||
define('DB_CHARSET', 'utf8mb4');
|
||||
|
||||
// ── Buat koneksi PDO ──────────────────────────────────────
|
||||
function getDB(): PDO {
|
||||
static $pdo = null;
|
||||
if ($pdo === null) {
|
||||
$dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET;
|
||||
$options = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
];
|
||||
try {
|
||||
$pdo = new PDO($dsn, DB_USER, DB_PASS, $options);
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Koneksi database gagal: ' . $e->getMessage()]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
return $pdo;
|
||||
}
|
||||
|
||||
// ── Header CORS & JSON (izinkan akses dari browser) ──────
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
// Handle preflight OPTIONS request
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(204);
|
||||
exit;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
// api/kavlings.php — GET / POST / DELETE kavling (polygon)
|
||||
require_once __DIR__ . '/config.php';
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
if ($method === 'GET') {
|
||||
$rows = getDB()->query("SELECT * FROM kavlings ORDER BY created_at DESC")->fetchAll();
|
||||
foreach ($rows as &$r) $r['area_m2'] = (int)$r['area_m2'];
|
||||
echo json_encode(['success'=>true,'data'=>$rows]); exit;
|
||||
}
|
||||
|
||||
if ($method === 'POST') {
|
||||
$b = json_decode(file_get_contents('php://input'),true);
|
||||
$name = trim($b['name'] ?? '');
|
||||
$status = trim($b['status'] ?? 'SHM');
|
||||
$area = (int)($b['area_m2'] ?? 0);
|
||||
$coords = $b['coords'] ?? '[]';
|
||||
$allowed = ['SHM','HGB','HGU','HP'];
|
||||
if (!$name){ http_response_code(400); echo json_encode(['error'=>'Nama wajib diisi']); exit; }
|
||||
if (!in_array($status,$allowed)) $status='SHM';
|
||||
$pdo = getDB();
|
||||
$stmt = $pdo->prepare("INSERT INTO kavlings (name,status,area_m2,coords) VALUES (:n,:s,:a,:c)");
|
||||
$stmt->execute([':n'=>$name,':s'=>$status,':a'=>$area,':c'=>$coords]);
|
||||
$id = $pdo->lastInsertId();
|
||||
$row = $pdo->query("SELECT * FROM kavlings WHERE id=$id")->fetch();
|
||||
$row['area_m2']=(int)$row['area_m2'];
|
||||
http_response_code(201); echo json_encode(['success'=>true,'data'=>$row]); exit;
|
||||
}
|
||||
|
||||
if ($method === 'DELETE') {
|
||||
$id=(int)($_GET['id']??0);
|
||||
if($id<=0){http_response_code(400);echo json_encode(['error'=>'ID tidak valid']);exit;}
|
||||
$stmt=getDB()->prepare("DELETE FROM kavlings WHERE id=:id");
|
||||
$stmt->execute([':id'=>$id]);
|
||||
if($stmt->rowCount()===0){http_response_code(404);echo json_encode(['error'=>'Tidak ditemukan']);exit;}
|
||||
echo json_encode(['success'=>true,'message'=>'Kavling dihapus']); exit;
|
||||
}
|
||||
|
||||
http_response_code(405); echo json_encode(['error'=>'Method tidak diizinkan']);
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
// ============================================================
|
||||
// api/locations.php
|
||||
// GET → ambil semua lokasi
|
||||
// POST → simpan lokasi baru
|
||||
// PATCH ?id=N → update koordinat (pindah pin)
|
||||
// DELETE ?id=N → hapus lokasi
|
||||
// ============================================================
|
||||
|
||||
require_once __DIR__ . '/config.php';
|
||||
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
// ── GET ───────────────────────────────────────────────────
|
||||
if ($method === 'GET') {
|
||||
$pdo = getDB();
|
||||
$rows = $pdo->query("SELECT * FROM locations ORDER BY created_at DESC")->fetchAll();
|
||||
foreach ($rows as &$row) {
|
||||
$row['lat'] = (float) $row['lat'];
|
||||
$row['lng'] = (float) $row['lng'];
|
||||
$row['is24h'] = (int) $row['is24h'];
|
||||
}
|
||||
echo json_encode(['success' => true, 'data' => $rows]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── POST: simpan baru ─────────────────────────────────────
|
||||
if ($method === 'POST') {
|
||||
$body = json_decode(file_get_contents('php://input'), true);
|
||||
$name = trim($body['name'] ?? '');
|
||||
$phone = trim($body['phone'] ?? '');
|
||||
$is24h = isset($body['is24h']) ? (int)(bool)$body['is24h'] : 0;
|
||||
$lat = isset($body['lat']) ? (float)$body['lat'] : null;
|
||||
$lng = isset($body['lng']) ? (float)$body['lng'] : null;
|
||||
|
||||
if ($name === '') { http_response_code(400); echo json_encode(['error'=>'Nama wajib diisi']); exit; }
|
||||
if ($lat === null || $lng === null) { http_response_code(400); echo json_encode(['error'=>'Koordinat wajib diisi']); exit; }
|
||||
|
||||
$pdo = getDB();
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO locations (name, phone, is24h, lat, lng) VALUES (:name,:phone,:is24h,:lat,:lng)"
|
||||
);
|
||||
$stmt->execute([':name'=>$name, ':phone'=>$phone, ':is24h'=>$is24h, ':lat'=>$lat, ':lng'=>$lng]);
|
||||
|
||||
$newId = $pdo->lastInsertId();
|
||||
$newRow = $pdo->query("SELECT * FROM locations WHERE id = $newId")->fetch();
|
||||
$newRow['lat'] = (float)$newRow['lat'];
|
||||
$newRow['lng'] = (float)$newRow['lng'];
|
||||
$newRow['is24h'] = (int) $newRow['is24h'];
|
||||
|
||||
http_response_code(201);
|
||||
echo json_encode(['success' => true, 'data' => $newRow]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── PATCH: update koordinat setelah pin dipindah ──────────
|
||||
if ($method === 'PATCH') {
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||
$body = json_decode(file_get_contents('php://input'), true);
|
||||
$lat = isset($body['lat']) ? (float)$body['lat'] : null;
|
||||
$lng = isset($body['lng']) ? (float)$body['lng'] : null;
|
||||
|
||||
if ($id <= 0 || $lat === null || $lng === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'ID atau koordinat tidak valid']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = getDB();
|
||||
$stmt = $pdo->prepare("UPDATE locations SET lat=:lat, lng=:lng WHERE id=:id");
|
||||
$stmt->execute([':lat'=>$lat, ':lng'=>$lng, ':id'=>$id]);
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Koordinat diperbarui']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── DELETE ────────────────────────────────────────────────
|
||||
if ($method === 'DELETE') {
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||
if ($id <= 0) { http_response_code(400); echo json_encode(['error'=>'ID tidak valid']); exit; }
|
||||
|
||||
$pdo = getDB();
|
||||
$stmt = $pdo->prepare("DELETE FROM locations WHERE id = :id");
|
||||
$stmt->execute([':id' => $id]);
|
||||
|
||||
if ($stmt->rowCount() === 0) { http_response_code(404); echo json_encode(['error'=>'Data tidak ditemukan']); exit; }
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Data dihapus']);
|
||||
exit;
|
||||
}
|
||||
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method tidak diizinkan']);
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
// api/roads.php — GET / POST / DELETE jalan (polyline)
|
||||
require_once __DIR__ . '/config.php';
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
if ($method === 'GET') {
|
||||
$rows = getDB()->query("SELECT * FROM roads ORDER BY created_at DESC")->fetchAll();
|
||||
foreach ($rows as &$r) $r['length_m'] = (int)$r['length_m'];
|
||||
echo json_encode(['success'=>true,'data'=>$rows]); exit;
|
||||
}
|
||||
|
||||
if ($method === 'POST') {
|
||||
$b = json_decode(file_get_contents('php://input'),true);
|
||||
$name = trim($b['name'] ?? '');
|
||||
$status = trim($b['status'] ?? 'kabupaten');
|
||||
$len = (int)($b['length_m'] ?? 0);
|
||||
$coords = $b['coords'] ?? '[]';
|
||||
$allowed = ['nasional','provinsi','kabupaten'];
|
||||
if (!$name){ http_response_code(400); echo json_encode(['error'=>'Nama wajib diisi']); exit; }
|
||||
if (!in_array($status,$allowed)) $status='kabupaten';
|
||||
$pdo = getDB();
|
||||
$stmt = $pdo->prepare("INSERT INTO roads (name,status,length_m,coords) VALUES (:n,:s,:l,:c)");
|
||||
$stmt->execute([':n'=>$name,':s'=>$status,':l'=>$len,':c'=>$coords]);
|
||||
$id = $pdo->lastInsertId();
|
||||
$row = $pdo->query("SELECT * FROM roads WHERE id=$id")->fetch();
|
||||
$row['length_m']=(int)$row['length_m'];
|
||||
http_response_code(201); echo json_encode(['success'=>true,'data'=>$row]); exit;
|
||||
}
|
||||
|
||||
if ($method === 'DELETE') {
|
||||
$id=(int)($_GET['id']??0);
|
||||
if($id<=0){http_response_code(400);echo json_encode(['error'=>'ID tidak valid']);exit;}
|
||||
$stmt=getDB()->prepare("DELETE FROM roads WHERE id=:id");
|
||||
$stmt->execute([':id'=>$id]);
|
||||
if($stmt->rowCount()===0){http_response_code(404);echo json_encode(['error'=>'Tidak ditemukan']);exit;}
|
||||
echo json_encode(['success'=>true,'message'=>'Jalan dihapus']); exit;
|
||||
}
|
||||
|
||||
http_response_code(405); echo json_encode(['error'=>'Method tidak diizinkan']);
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
// ============================================================
|
||||
// api/search.php
|
||||
// GET ?q=keyword → cari di locations, roads, kavlings
|
||||
// ============================================================
|
||||
require_once __DIR__ . '/config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method tidak diizinkan']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$q = trim($_GET['q'] ?? '');
|
||||
if ($q === '') {
|
||||
echo json_encode(['success' => true, 'data' => ['points'=>[],'roads'=>[],'kavlings'=>[]]]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = getDB();
|
||||
$like = '%' . $q . '%';
|
||||
|
||||
// Points
|
||||
$stmt = $pdo->prepare("SELECT * FROM locations WHERE name LIKE :q OR phone LIKE :q2 ORDER BY name LIMIT 10");
|
||||
$stmt->execute([':q' => $like, ':q2' => $like]);
|
||||
$points = $stmt->fetchAll();
|
||||
foreach ($points as &$r) { $r['lat']=(float)$r['lat']; $r['lng']=(float)$r['lng']; $r['is24h']=(int)$r['is24h']; }
|
||||
|
||||
// Roads
|
||||
$stmt = $pdo->prepare("SELECT id,name,status,length_m,created_at FROM roads WHERE name LIKE :q OR status LIKE :q2 ORDER BY name LIMIT 8");
|
||||
$stmt->execute([':q' => $like, ':q2' => $like]);
|
||||
$roads = $stmt->fetchAll();
|
||||
foreach ($roads as &$r) { $r['length_m']=(int)$r['length_m']; }
|
||||
|
||||
// Kavlings
|
||||
$stmt = $pdo->prepare("SELECT id,name,status,area_m2,created_at FROM kavlings WHERE name LIKE :q OR status LIKE :q2 ORDER BY name LIMIT 8");
|
||||
$stmt->execute([':q' => $like, ':q2' => $like]);
|
||||
$kavlings = $stmt->fetchAll();
|
||||
foreach ($kavlings as &$r) { $r['area_m2']=(int)$r['area_m2']; }
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'data' => ['points' => $points, 'roads' => $roads, 'kavlings' => $kavlings]
|
||||
]);
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
$conn = new mysqli("localhost", "root", "", "gis_project");
|
||||
|
||||
$id = $_POST['id'];
|
||||
|
||||
if ($id) {
|
||||
$stmt = $conn->prepare("DELETE FROM worship_places WHERE id = ?");
|
||||
$stmt->bind_param("i", $id);
|
||||
if ($stmt->execute()) {
|
||||
echo "Deleted";
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
$conn = new mysqli("localhost", "root", "", "gis_project");
|
||||
|
||||
$result = $conn->query("SELECT * FROM worship_places");
|
||||
$places = [];
|
||||
|
||||
while($row = $result->fetch_assoc()) {
|
||||
$places[] = $row;
|
||||
}
|
||||
|
||||
echo json_encode($places);
|
||||
$conn->close();
|
||||
?>
|
||||
+1233
File diff suppressed because it is too large
Load Diff
+288
@@ -0,0 +1,288 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>GIS Pontianak - Integrated SPBU Management</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=DM+Sans:wght@300;400;500;600&display=swap" rel="stylesheet"/>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #0d1117; --surface: #161b22; --border: #30363d;
|
||||
--accent: #00d4aa; --accent2: #ff6b35; --text: #e6edf3;
|
||||
--muted: #8b949e; --danger: #f85149; --panel-w: 340px;
|
||||
}
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body { font-family:'DM Sans',sans-serif; background:var(--bg); color:var(--text); height:100vh; display:flex; flex-direction:column; overflow:hidden; }
|
||||
|
||||
/* ── HEADER ── */
|
||||
header { height:52px; background:var(--surface); border-bottom:1px solid var(--border); display:flex; align-items:center; padding:0 16px; gap:10px; z-index:1000; flex-shrink:0; }
|
||||
.logo { font-family:'Space Mono',monospace; font-size:15px; font-weight:700; color:var(--accent); letter-spacing:-.5px; }
|
||||
.logo span { color:var(--accent2); }
|
||||
.badge { background:rgba(0,212,170,.12); color:var(--accent); font-size:11px; font-weight:600; padding:3px 8px; border-radius:4px; border:1px solid rgba(0,212,170,.3); }
|
||||
|
||||
/* Search Bar */
|
||||
.search-wrap { position: relative; flex: 1; max-width: 320px; }
|
||||
#search-input { width:100%; background:var(--bg); border:1px solid var(--border); border-radius:8px; padding:7px 34px; font-size:13px; color:var(--text); outline:none; }
|
||||
#search-dropdown { display:none; position:absolute; top:calc(100% + 6px); left:0; right:0; background:var(--surface); border:1px solid var(--border); border-radius:10px; z-index:9000; max-height:380px; overflow-y:auto; }
|
||||
#search-dropdown.open { display:block; }
|
||||
|
||||
/* Toolbar */
|
||||
.draw-toolbar { display:flex; align-items:center; gap:6px; margin-left:auto; }
|
||||
.tool-btn { display:flex; align-items:center; gap:5px; padding:6px 11px; border-radius:6px; border:1px solid var(--border); background:var(--bg); color:var(--muted); font-size:12px; font-weight:600; cursor:pointer; transition:all .2s; }
|
||||
.tool-btn:hover { border-color:var(--accent); color:var(--accent); }
|
||||
.tool-btn.active { border-color:var(--accent2); color:#fff; background:var(--accent2); }
|
||||
|
||||
/* ── MAIN LAYOUT ── */
|
||||
.main { flex:1; display:flex; overflow:hidden; }
|
||||
#map { flex:1; }
|
||||
|
||||
/* Panel & Tabs */
|
||||
.panel { width:var(--panel-w); background:var(--surface); border-left:1px solid var(--border); display:flex; flex-direction:column; flex-shrink:0; }
|
||||
.panel-tabs { display:flex; border-bottom:1px solid var(--border); }
|
||||
.tab-btn { flex:1; padding:10px; font-size:11px; font-weight:700; text-transform:uppercase; background:none; border:none; color:var(--muted); cursor:pointer; border-bottom:2px solid transparent; }
|
||||
.tab-btn.active { color:var(--accent); border-bottom-color:var(--accent); }
|
||||
.tab-pane { display:none; flex:1; flex-direction:column; overflow:hidden; }
|
||||
.tab-pane.active { display:flex; }
|
||||
|
||||
.records-list { flex:1; overflow-y:auto; padding:10px; display:flex; flex-direction:column; gap:7px; }
|
||||
.record-card { background:var(--bg); border:1px solid var(--border); border-radius:8px; padding:11px 13px; cursor:pointer; position:relative; }
|
||||
.rc-name { font-weight:600; font-size:13px; color:var(--text); }
|
||||
.rc-sub { font-size:11px; color:var(--muted); margin-top:2px; font-family:'Space Mono',monospace; }
|
||||
|
||||
/* Modal */
|
||||
.modal-overlay { display:none; position:fixed; inset:0; background:rgba(0,0,0,.6); z-index:9999; align-items:center; justify-content:center; backdrop-filter:blur(4px); }
|
||||
.modal-overlay.active { display:flex; }
|
||||
.modal { background:var(--surface); border:1px solid var(--border); border-radius:14px; width:400px; padding:20px; }
|
||||
.field { margin-bottom: 15px; }
|
||||
.field label { display:block; font-size:11px; color:var(--muted); margin-bottom:5px; }
|
||||
.field input, .field select { width:100%; background:var(--bg); border:1px solid var(--border); padding:9px; color:#fff; border-radius:7px; outline:none; }
|
||||
|
||||
/* Drawing Hint */
|
||||
#draw-hint { display:none; position:fixed; bottom:60px; left:50%; transform:translateX(-50%); background:var(--surface); border:1px solid var(--border); border-radius:10px; padding:10px 20px; z-index:5000; }
|
||||
#draw-hint.visible { display:flex; align-items:center; gap:10px; }
|
||||
|
||||
/* Leaflet Control Custom */
|
||||
.leaflet-control-layers { background: var(--surface) !important; color: var(--text) !important; border: 1px solid var(--border) !important; border-radius: 8px !important; }
|
||||
|
||||
#toast { position:fixed; bottom:24px; left:50%; transform:translateX(-50%); background:var(--accent); color:#0d1117; padding:10px 22px; border-radius:8px; opacity:0; transition:0.3s; z-index:10000; font-weight:700; }
|
||||
#toast.show { opacity:1; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<div class="logo">GIS<span>.</span>PTK</div>
|
||||
<span class="badge">Pontianak</span>
|
||||
|
||||
<div class="search-wrap">
|
||||
<input type="text" id="search-input" placeholder="Cari lokasi, jalan, atau kavling..."/>
|
||||
<div id="search-dropdown"></div>
|
||||
</div>
|
||||
|
||||
<div class="draw-toolbar">
|
||||
<button class="tool-btn active" id="tool-point">📍 Titik</button>
|
||||
<button class="tool-btn" id="tool-polyline">〰️ Jalan</button>
|
||||
<button class="tool-btn" id="tool-polygon">⬡ Kavling</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="main">
|
||||
<div id="map"></div>
|
||||
<aside class="panel">
|
||||
<div class="panel-tabs">
|
||||
<button class="tab-btn active" data-tab="tab-points">📍 Titik</button>
|
||||
<button class="tab-btn" data-tab="tab-roads">〰️ Jalan</button>
|
||||
<button class="tab-btn" data-tab="tab-kavling">⬡ Kavling</button>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane active" id="tab-points">
|
||||
<div class="records-list" id="records-list"></div>
|
||||
</div>
|
||||
<div class="tab-pane" id="tab-roads">
|
||||
<div class="records-list" id="roads-list"></div>
|
||||
</div>
|
||||
<div class="tab-pane" id="tab-kavling">
|
||||
<div class="records-list" id="kavling-list"></div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="modal-point">
|
||||
<div class="modal">
|
||||
<h3 style="margin-bottom:15px">📍 Tambah SPBU</h3>
|
||||
<div class="field"><label>Nama Lokasi</label><input type="text" id="pt-name"/></div>
|
||||
<div class="field"><label>Telepon</label><input type="tel" id="pt-phone"/></div>
|
||||
<div class="field">
|
||||
<label>Buka 24 Jam?</label>
|
||||
<select id="pt-is24h">
|
||||
<option value="1">Ya, 24 Jam</option>
|
||||
<option value="0">Tidak</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="tool-btn active" id="pt-save" style="width:100%; justify-content:center; padding:12px">Simpan</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="draw-hint">
|
||||
<span id="draw-hint-text">Klik peta untuk menggambar</span>
|
||||
<button class="tool-btn active" id="dh-finish">Selesai</button>
|
||||
<button class="tool-btn" id="dh-cancel">Batal</button>
|
||||
</div>
|
||||
|
||||
<div id="toast"></div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js"></script>
|
||||
<script>
|
||||
// --- CONFIG & STATE ---
|
||||
const PONTIANAK = [-0.0264, 109.3425];
|
||||
const API_POINT = 'api/locations.php';
|
||||
const API_ROAD = 'api/roads.php';
|
||||
const API_KAVLING = 'api/kavlings.php';
|
||||
|
||||
let currentMode = 'point';
|
||||
let drawingPoints = [];
|
||||
let previewLayer = null;
|
||||
let pendingLatLng = null;
|
||||
const markerMap = {}, roadMap = {}, kavlingMap = {};
|
||||
let searchCache = { points:[], roads:[], kavlings:[] };
|
||||
|
||||
// --- INITIALIZE MAP & LAYER GROUPS ---
|
||||
const map = L.map('map').setView(PONTIANAK, 14);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
|
||||
|
||||
// LAYER GROUPS (TUGAS)
|
||||
const layerSpbu24 = L.layerGroup().addTo(map);
|
||||
const layerSpbuReguler = L.layerGroup().addTo(map);
|
||||
|
||||
const overlayMaps = {
|
||||
"SPBU Buka 24 Jam": layerSpbu24,
|
||||
"SPBU Tidak Buka 24 Jam": layerSpbuReguler
|
||||
};
|
||||
L.control.layers(null, overlayMaps, { collapsed: false }).addTo(map);
|
||||
|
||||
// --- API HELPERS ---
|
||||
const api = {
|
||||
async get(url) { const r=await fetch(url); const j=await r.json(); return j.data; },
|
||||
async post(url,body) { const r=await fetch(url,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}); return (await r.json()).data; },
|
||||
async del(url) { await fetch(url,{method:'DELETE'}); }
|
||||
};
|
||||
|
||||
// --- RESTORED DRAWING TOOLS LOGIC ---
|
||||
document.getElementById('tool-point').onclick = () => setMode('point');
|
||||
document.getElementById('tool-polyline').onclick = () => setMode('polyline');
|
||||
document.getElementById('tool-polygon').onclick = () => setMode('polygon');
|
||||
|
||||
function setMode(mode) {
|
||||
currentMode = mode;
|
||||
drawingPoints = [];
|
||||
if(previewLayer) map.removeLayer(previewLayer);
|
||||
document.querySelectorAll('.tool-btn').forEach(b => b.classList.remove('active'));
|
||||
document.getElementById('tool-' + mode).classList.add('active');
|
||||
document.getElementById('draw-hint').classList.toggle('visible', mode !== 'point');
|
||||
}
|
||||
|
||||
map.on('click', (e) => {
|
||||
if(currentMode === 'point') {
|
||||
pendingLatLng = e.latlng;
|
||||
document.getElementById('modal-point').classList.add('active');
|
||||
} else {
|
||||
drawingPoints.push(e.latlng);
|
||||
updatePreview();
|
||||
}
|
||||
});
|
||||
|
||||
function updatePreview() {
|
||||
if(previewLayer) map.removeLayer(previewLayer);
|
||||
if(drawingPoints.length < 2) return;
|
||||
previewLayer = currentMode === 'polyline' ? L.polyline(drawingPoints, {color: '#ff6b35'}) : L.polygon(drawingPoints, {color: '#a371f7'});
|
||||
previewLayer.addTo(map);
|
||||
}
|
||||
|
||||
// --- RESTORED SEARCH LOGIC ---
|
||||
document.getElementById('search-input').oninput = (e) => {
|
||||
const q = e.target.value.toLowerCase();
|
||||
const drop = document.getElementById('search-dropdown');
|
||||
if(!q) { drop.classList.remove('open'); return; }
|
||||
|
||||
let html = '';
|
||||
const filtered = searchCache.points.filter(p => p.name.toLowerCase().includes(q));
|
||||
filtered.forEach(p => {
|
||||
html += `<div style="padding:10px; cursor:pointer; border-bottom:1px solid var(--border)" onclick="focusLoc(${p.lat}, ${p.lng}, ${p.id})">📍 ${p.name}</div>`;
|
||||
});
|
||||
drop.innerHTML = html || '<div style="padding:10px">Tidak ditemukan</div>';
|
||||
drop.classList.add('open');
|
||||
};
|
||||
|
||||
window.focusLoc = (lat, lng, id) => {
|
||||
map.flyTo([lat, lng], 17);
|
||||
if(markerMap[id]) markerMap[id].openPopup();
|
||||
document.getElementById('search-dropdown').classList.remove('open');
|
||||
};
|
||||
|
||||
// --- REFRESH LISTS & LAYER GROUPS ---
|
||||
function placeMarker(rec) {
|
||||
const is24 = rec.is24h == 1;
|
||||
const color = is24 ? '#56d364' : '#ff7b72';
|
||||
const icon = L.divIcon({ html: `<div style="background:${color}; width:12px; height:12px; border-radius:50%; border:2px solid #fff;"></div>`, className: '', iconSize:[12,12]});
|
||||
|
||||
const marker = L.marker([rec.lat, rec.lng], {icon}).bindPopup(`<b>${rec.name}</b><br>${is24 ? '✅ 24 Jam' : '❌ Reguler'}`);
|
||||
|
||||
// Logic Layer Group
|
||||
if(is24) marker.addTo(layerSpbu24); else marker.addTo(layerSpbuReguler);
|
||||
|
||||
markerMap[rec.id] = marker;
|
||||
}
|
||||
|
||||
async function refreshAll() {
|
||||
const pts = await api.get(API_POINT);
|
||||
searchCache.points = pts;
|
||||
layerSpbu24.clearLayers();
|
||||
layerSpbuReguler.clearLayers();
|
||||
|
||||
const list = document.getElementById('records-list');
|
||||
list.innerHTML = '';
|
||||
pts.forEach(p => {
|
||||
placeMarker(p);
|
||||
list.innerHTML += `<div class="record-card" onclick="map.panTo([${p.lat},${p.lng}])">
|
||||
<div class="rc-name">${p.name}</div>
|
||||
<div class="rc-sub">${p.is24h == 1 ? 'Buka 24 Jam' : 'Reguler'}</div>
|
||||
<button style="position:absolute; top:8px; right:8px; background:none; border:none; color:var(--danger); cursor:pointer" onclick="event.stopPropagation(); deletePt(${p.id})">🗑</button>
|
||||
</div>`;
|
||||
});
|
||||
}
|
||||
|
||||
window.deletePt = async (id) => {
|
||||
await api.del(API_POINT + '?id=' + id);
|
||||
if(markerMap[id]) {
|
||||
layerSpbu24.removeLayer(markerMap[id]);
|
||||
layerSpbuReguler.removeLayer(markerMap[id]);
|
||||
}
|
||||
refreshAll();
|
||||
};
|
||||
|
||||
document.getElementById('pt-save').onclick = async () => {
|
||||
const name = document.getElementById('pt-name').value;
|
||||
const is24h = document.getElementById('pt-is24h').value;
|
||||
if(!name) return;
|
||||
await api.post(API_POINT, {name, is24h: parseInt(is24h), lat: pendingLatLng.lat, lng: pendingLatLng.lng});
|
||||
document.getElementById('modal-point').classList.remove('active');
|
||||
refreshAll();
|
||||
showToast('Lokasi disimpan');
|
||||
};
|
||||
|
||||
// --- TABS LOGIC ---
|
||||
document.querySelectorAll('.tab-btn').forEach(btn => {
|
||||
btn.onclick = () => {
|
||||
document.querySelectorAll('.tab-btn, .tab-pane').forEach(el => el.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
document.getElementById(btn.dataset.tab).classList.add('active');
|
||||
};
|
||||
});
|
||||
|
||||
function showToast(m) { const t=document.getElementById('toast'); t.textContent=m; t.classList.add('show'); setTimeout(()=>t.classList.remove('show'),2000); }
|
||||
|
||||
refreshAll();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
$conn = new mysqli("localhost", "root", "", "gis_project");
|
||||
|
||||
$name = $_POST['name'];
|
||||
$lat = $_POST['lat'];
|
||||
$lng = $_POST['lng'];
|
||||
$radius = $_POST['radius'];
|
||||
$type = $_POST['type'];
|
||||
$address = $_POST['address']; // Capture the address
|
||||
|
||||
$stmt = $conn->prepare("INSERT INTO worship_places (name, lat, lng, radius, type, address) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
$stmt->bind_param("ssdiss", $name, $lat, $lng, $radius, $type, $address);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
echo json_encode(["id" => $conn->insert_id]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
$conn = new mysqli("localhost", "root", "", "gis_project");
|
||||
$id = $_POST['id'];
|
||||
$rad = $_POST['radius'];
|
||||
|
||||
$stmt = $conn->prepare("UPDATE worship_places SET radius = ? WHERE id = ?");
|
||||
$stmt->bind_param("ii", $rad, $id);
|
||||
$stmt->execute();
|
||||
?>
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user