532 lines
17 KiB
PHP
532 lines
17 KiB
PHP
<?php
|
|
// ─── Matikan exception mysqli — wajib sebelum koneksi apapun ─────────────
|
|
mysqli_report(MYSQLI_REPORT_OFF);
|
|
|
|
// ─── Safe stats: selalu return nilai, tidak pernah crash ──────────────────
|
|
function safeCount($db, $table) {
|
|
try {
|
|
$conn = @new mysqli("localhost", "root", "", $db);
|
|
if (!$conn || $conn->connect_error) return null;
|
|
$conn->set_charset("utf8mb4");
|
|
$r = $conn->query("SELECT COUNT(*) AS c FROM `$table`");
|
|
$val = ($r) ? (int)$r->fetch_assoc()['c'] : null;
|
|
$conn->close();
|
|
return $val;
|
|
} catch (Throwable $e) { return null; }
|
|
}
|
|
|
|
function safeSum($db, $table, $col) {
|
|
try {
|
|
$conn = @new mysqli("localhost", "root", "", $db);
|
|
if (!$conn || $conn->connect_error) return null;
|
|
$conn->set_charset("utf8mb4");
|
|
$r = $conn->query("SELECT COALESCE(SUM(`$col`),0) AS s FROM `$table`");
|
|
$val = ($r) ? (float)$r->fetch_assoc()['s'] : null;
|
|
$conn->close();
|
|
return $val;
|
|
} catch (Throwable $e) { return null; }
|
|
}
|
|
|
|
function safeQuery($db, $sql) {
|
|
try {
|
|
$conn = @new mysqli("localhost", "root", "", $db);
|
|
if (!$conn || $conn->connect_error) return null;
|
|
$conn->set_charset("utf8mb4");
|
|
$r = $conn->query($sql);
|
|
$val = ($r) ? $r->fetch_assoc() : null;
|
|
$conn->close();
|
|
return $val;
|
|
} catch (Throwable $e) { return null; }
|
|
}
|
|
|
|
// Coba kedua nama database untuk kemiskinan
|
|
$dbPoverty = null;
|
|
foreach (['webgis_poverty', 'webgis'] as $dbName) {
|
|
try {
|
|
$test = @new mysqli("localhost", "root", "", $dbName);
|
|
if ($test && !$test->connect_error) { $dbPoverty = $dbName; $test->close(); break; }
|
|
} catch (Throwable $e) { continue; }
|
|
}
|
|
|
|
$stats = [
|
|
'jalan_ruas' => safeCount('jalan', 'jalan'),
|
|
'jalan_km' => round((safeSum('jalan', 'jalan', 'panjang_meter') ?? 0) / 1000, 2),
|
|
'parsil' => safeCount('jalan', 'parsil_tanah'),
|
|
'spbu_total' => safeCount('spbu', 'spbu'),
|
|
'spbu_24' => null,
|
|
'kk_miskin' => $dbPoverty ? safeCount($dbPoverty, 'penduduk_miskin') : null,
|
|
'log_bantuan' => $dbPoverty ? safeCount($dbPoverty, 'log_bantuan') : null,
|
|
'ibadah' => $dbPoverty ? safeCount($dbPoverty, 'rumah_ibadah') : null,
|
|
];
|
|
|
|
$spbuRow = safeQuery('spbu', "SELECT SUM(buka_24_jam=1) AS b FROM spbu");
|
|
if ($spbuRow) $stats['spbu_24'] = (int)$spbuRow['b'];
|
|
|
|
// Helper: tampilkan nilai atau dash
|
|
function sv($v, $suffix = '') {
|
|
if ($v === null) return '<span class="stat-na">—</span>';
|
|
return htmlspecialchars($v . $suffix);
|
|
}
|
|
|
|
$year = date('Y');
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="id">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>WebGIS Pontianak — Portal</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<style>
|
|
/* ── Reset ── */
|
|
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
|
|
|
/* ── Tokens ── */
|
|
:root {
|
|
--bg: #0b0e17;
|
|
--surf: #131722;
|
|
--surf2: #1a2133;
|
|
--bdr: rgba(255,255,255,.07);
|
|
--bdr2: rgba(255,255,255,.13);
|
|
--txt: #e8edf5;
|
|
--muted: #7a8499;
|
|
--j: #3b82f6;
|
|
--p: #10b981;
|
|
--s: #f97316;
|
|
--k: #a855f7;
|
|
--gj: rgba(59,130,246,.15);
|
|
--gp: rgba(16,185,129,.15);
|
|
--gs: rgba(249,115,22,.15);
|
|
--gk: rgba(168,85,247,.15);
|
|
--r: 16px;
|
|
--r-sm: 10px;
|
|
}
|
|
|
|
/* ── Base ── */
|
|
html { scroll-behavior: smooth; }
|
|
body {
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
background: var(--bg);
|
|
color: var(--txt);
|
|
min-height: 100vh;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
/* ── Ambient background ── */
|
|
.bg-ambient {
|
|
position: fixed; inset: 0; z-index: 0; pointer-events: none;
|
|
background:
|
|
radial-gradient(ellipse 60% 50% at 15% 10%, rgba(59,130,246,.08) 0%, transparent 70%),
|
|
radial-gradient(ellipse 50% 40% at 85% 80%, rgba(168,85,247,.08) 0%, transparent 70%),
|
|
radial-gradient(ellipse 40% 35% at 78% 12%, rgba(16,185,129,.05) 0%, transparent 60%);
|
|
}
|
|
.bg-dots {
|
|
position: fixed; inset: 0; z-index: 0; pointer-events: none; opacity: .3;
|
|
background-image: radial-gradient(circle, rgba(255,255,255,.15) 1px, transparent 1px);
|
|
background-size: 28px 28px;
|
|
}
|
|
|
|
/* ── Layout ── */
|
|
.wrap {
|
|
position: relative; z-index: 1;
|
|
max-width: 1100px;
|
|
margin: 0 auto;
|
|
padding: 0 24px 80px;
|
|
}
|
|
|
|
/* ── Header ── */
|
|
header { padding: 52px 0 8px; }
|
|
|
|
.eyebrow {
|
|
display: flex; align-items: center; gap: 8px;
|
|
font-size: 11px; font-weight: 700;
|
|
letter-spacing: .12em; text-transform: uppercase;
|
|
color: var(--muted); margin-bottom: 14px;
|
|
}
|
|
.eyebrow-dot {
|
|
width: 7px; height: 7px; border-radius: 50%;
|
|
background: var(--p);
|
|
box-shadow: 0 0 0 3px rgba(16,185,129,.2);
|
|
animation: livepulse 2.2s ease-in-out infinite;
|
|
}
|
|
@keyframes livepulse {
|
|
0%,100% { box-shadow: 0 0 0 3px rgba(16,185,129,.2); }
|
|
50% { box-shadow: 0 0 0 7px rgba(16,185,129,.0); }
|
|
}
|
|
|
|
.title {
|
|
font-size: clamp(32px, 5vw, 52px);
|
|
font-weight: 800;
|
|
line-height: 1.06;
|
|
letter-spacing: -.025em;
|
|
color: #fff;
|
|
margin-bottom: 14px;
|
|
}
|
|
.title-grad {
|
|
background: linear-gradient(95deg, var(--j) 0%, var(--k) 100%);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
.subtitle {
|
|
font-size: 15px; color: var(--muted);
|
|
max-width: 540px; line-height: 1.65; font-weight: 400;
|
|
margin-bottom: 40px;
|
|
}
|
|
|
|
/* ── Section label ── */
|
|
.sec-label {
|
|
font-size: 10px; font-weight: 700;
|
|
letter-spacing: .12em; text-transform: uppercase;
|
|
color: var(--muted); margin-bottom: 16px;
|
|
display: flex; align-items: center; gap: 10px;
|
|
}
|
|
.sec-label::after { content:''; flex:1; height:1px; background:var(--bdr); }
|
|
|
|
/* ── Summary bar ── */
|
|
.summary-bar {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 12px;
|
|
margin-bottom: 36px;
|
|
}
|
|
@media (max-width:700px) { .summary-bar { grid-template-columns: repeat(2,1fr); } }
|
|
@media (max-width:400px) { .summary-bar { grid-template-columns: 1fr; } }
|
|
|
|
.sum-card {
|
|
background: var(--surf);
|
|
border: 1px solid var(--bdr);
|
|
border-radius: var(--r-sm);
|
|
padding: 18px 20px;
|
|
position: relative; overflow: hidden;
|
|
}
|
|
.sum-card::before {
|
|
content:''; position:absolute;
|
|
left:0; top:0; bottom:0; width:3px;
|
|
background: var(--c);
|
|
border-radius: 3px 0 0 3px;
|
|
}
|
|
.sum-num {
|
|
font-size: 26px; font-weight: 800;
|
|
color: var(--c); line-height: 1; margin-bottom: 6px;
|
|
}
|
|
.sum-lbl {
|
|
font-size: 10px; font-weight: 600;
|
|
text-transform: uppercase; letter-spacing: .07em;
|
|
color: var(--muted);
|
|
}
|
|
.stat-na { opacity: .35; font-weight: 400; }
|
|
|
|
/* ── Divider ── */
|
|
.divider { height:1px; background:var(--bdr); margin: 4px 0 28px; }
|
|
|
|
/* ── Apps grid ── */
|
|
.apps-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2,1fr);
|
|
gap: 16px;
|
|
}
|
|
@media (max-width:640px) { .apps-grid { grid-template-columns: 1fr; } }
|
|
|
|
/* ── App card ── */
|
|
.app-card {
|
|
background: var(--surf);
|
|
border: 1px solid var(--bdr);
|
|
border-radius: var(--r);
|
|
padding: 26px 26px 66px;
|
|
text-decoration: none;
|
|
color: var(--txt);
|
|
display: block;
|
|
position: relative;
|
|
overflow: hidden;
|
|
transition: border-color .2s, transform .18s, background .2s;
|
|
}
|
|
.app-card::after {
|
|
content:''; position:absolute;
|
|
top:-70px; right:-70px;
|
|
width:180px; height:180px;
|
|
border-radius:50%;
|
|
background: var(--cg);
|
|
transition: opacity .3s, transform .3s;
|
|
opacity: .6;
|
|
}
|
|
.app-card:hover {
|
|
border-color: var(--ca);
|
|
background: var(--surf2);
|
|
transform: translateY(-3px);
|
|
}
|
|
.app-card:hover::after { opacity:1; transform:scale(1.15); }
|
|
|
|
/* Card accent variables per type */
|
|
.c-jalan { --ca:var(--j); --cg:var(--gj); }
|
|
.c-penduduk { --ca:var(--p); --cg:var(--gp); }
|
|
.c-spbu { --ca:var(--s); --cg:var(--gs); }
|
|
.c-kemiskin { --ca:var(--k); --cg:var(--gk); }
|
|
|
|
/* Card top row */
|
|
.card-head {
|
|
display: flex; align-items: flex-start;
|
|
justify-content: space-between;
|
|
margin-bottom: 18px;
|
|
position: relative; z-index:1;
|
|
}
|
|
.card-ico {
|
|
width: 46px; height: 46px; border-radius: 13px;
|
|
background: var(--cg);
|
|
border: 1px solid rgba(255,255,255,.09);
|
|
display: flex; align-items: center; justify-content: center;
|
|
font-size: 20px; color: var(--ca);
|
|
}
|
|
.card-tag {
|
|
font-size: 9px; font-weight: 700;
|
|
letter-spacing: .07em; text-transform: uppercase;
|
|
padding: 4px 10px; border-radius: 99px;
|
|
border: 1px solid var(--ca);
|
|
color: var(--ca); opacity: .75;
|
|
}
|
|
|
|
/* Card body */
|
|
.card-body { position: relative; z-index:1; margin-bottom: 18px; }
|
|
.card-name {
|
|
font-size: 18px; font-weight: 700;
|
|
color: #fff; line-height: 1.2; margin-bottom: 8px;
|
|
}
|
|
.card-desc {
|
|
font-size: 12.5px; color: var(--muted);
|
|
line-height: 1.6;
|
|
}
|
|
|
|
/* Card mini-stats */
|
|
.card-stats {
|
|
display: flex; gap: 18px;
|
|
padding-top: 16px;
|
|
border-top: 1px solid var(--bdr);
|
|
position: relative; z-index:1;
|
|
}
|
|
.cs-val {
|
|
font-size: 17px; font-weight: 700;
|
|
color: var(--ca); line-height: 1; margin-bottom: 4px;
|
|
}
|
|
.cs-lbl {
|
|
font-size: 9px; font-weight: 600;
|
|
text-transform: uppercase; letter-spacing: .05em;
|
|
color: var(--muted);
|
|
}
|
|
|
|
/* Arrow button */
|
|
.card-arrow {
|
|
position: absolute; bottom: 22px; right: 22px; z-index:2;
|
|
width: 34px; height: 34px; border-radius: 50%;
|
|
background: rgba(255,255,255,.04);
|
|
border: 1px solid var(--bdr2);
|
|
display: flex; align-items: center; justify-content: center;
|
|
font-size: 12px; color: var(--muted);
|
|
transition: all .2s;
|
|
}
|
|
.app-card:hover .card-arrow {
|
|
background: var(--ca);
|
|
border-color: var(--ca);
|
|
color: #fff;
|
|
transform: translateX(2px);
|
|
}
|
|
|
|
/* ── Footer ── */
|
|
footer {
|
|
margin-top: 60px; padding-top: 22px;
|
|
border-top: 1px solid var(--bdr);
|
|
display: flex; justify-content: space-between;
|
|
align-items: center; flex-wrap: wrap; gap: 12px;
|
|
}
|
|
.ft-l { font-size: 12px; color: var(--muted); }
|
|
.ft-r { display:flex; gap:7px; align-items:center; }
|
|
.ft-dot { width:5px; height:5px; border-radius:50%; background:var(--p); }
|
|
.ft-txt { font-size: 11px; color: var(--muted); }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="bg-ambient"></div>
|
|
<div class="bg-dots"></div>
|
|
|
|
<div class="wrap">
|
|
|
|
<!-- Header -->
|
|
<header>
|
|
<div class="eyebrow">
|
|
<span class="eyebrow-dot"></span>
|
|
WebGIS Kota Pontianak
|
|
</div>
|
|
<h1 class="title">
|
|
Portal <span class="title-grad">Sistem Informasi</span><br>Geospasial
|
|
</h1>
|
|
<p class="subtitle">
|
|
Platform terpadu pemetaan infrastruktur, kependudukan, energi, dan kemiskinan
|
|
Kota Pontianak — Kalimantan Barat.
|
|
</p>
|
|
</header>
|
|
|
|
<!-- Ringkasan Data -->
|
|
<div class="sec-label">Ringkasan Data</div>
|
|
<div class="summary-bar">
|
|
<div class="sum-card" style="--c:var(--j)">
|
|
<div class="sum-num"><?= sv($stats['jalan_ruas']) ?></div>
|
|
<div class="sum-lbl">Ruas Jalan</div>
|
|
</div>
|
|
<div class="sum-card" style="--c:var(--p)">
|
|
<div class="sum-num">6</div>
|
|
<div class="sum-lbl">Kecamatan</div>
|
|
</div>
|
|
<div class="sum-card" style="--c:var(--s)">
|
|
<div class="sum-num"><?= sv($stats['spbu_total']) ?></div>
|
|
<div class="sum-lbl">SPBU Terdaftar</div>
|
|
</div>
|
|
<div class="sum-card" style="--c:var(--k)">
|
|
<div class="sum-num"><?= sv($stats['kk_miskin']) ?></div>
|
|
<div class="sum-lbl">KK Miskin</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- App Cards -->
|
|
<div class="sec-label">Aplikasi Tersedia</div>
|
|
<div class="apps-grid">
|
|
|
|
<!-- Jalan -->
|
|
<a href="jalan/index.php" class="app-card c-jalan">
|
|
<div class="card-head">
|
|
<div class="card-ico"><i class="fas fa-road"></i></div>
|
|
<span class="card-tag">GIS Layer</span>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="card-name">Pemetaan Jalan<br>& Persil Tanah</div>
|
|
<div class="card-desc">
|
|
Peta interaktif ruas jalan (polyline) dan persil tanah (polygon)
|
|
dengan fitur CRUD dan penggambaran langsung di peta.
|
|
</div>
|
|
</div>
|
|
<div class="card-stats">
|
|
<div>
|
|
<div class="cs-val"><?= sv($stats['jalan_ruas']) ?></div>
|
|
<div class="cs-lbl">Ruas Jalan</div>
|
|
</div>
|
|
<div>
|
|
<div class="cs-val"><?= sv($stats['jalan_km'], ' km') ?></div>
|
|
<div class="cs-lbl">Panjang Total</div>
|
|
</div>
|
|
<div>
|
|
<div class="cs-val"><?= sv($stats['parsil']) ?></div>
|
|
<div class="cs-lbl">Persil Tanah</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-arrow"><i class="fas fa-arrow-right"></i></div>
|
|
</a>
|
|
|
|
<!-- Penduduk -->
|
|
<a href="penduduk/index.php" class="app-card c-penduduk">
|
|
<div class="card-head">
|
|
<div class="card-ico"><i class="fas fa-users"></i></div>
|
|
<span class="card-tag">Choropleth</span>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="card-name">Kepadatan<br>Penduduk</div>
|
|
<div class="card-desc">
|
|
Visualisasi choropleth kepadatan penduduk per kecamatan di
|
|
Kota Pontianak dengan legenda gradasi warna interaktif.
|
|
</div>
|
|
</div>
|
|
<div class="card-stats">
|
|
<div>
|
|
<div class="cs-val">6</div>
|
|
<div class="cs-lbl">Kecamatan</div>
|
|
</div>
|
|
<div>
|
|
<div class="cs-val">9.33</div>
|
|
<div class="cs-lbl">Kepadatan Maks</div>
|
|
</div>
|
|
<div>
|
|
<div class="cs-val">UTM</div>
|
|
<div class="cs-lbl">Proyeksi</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-arrow"><i class="fas fa-arrow-right"></i></div>
|
|
</a>
|
|
|
|
<!-- SPBU -->
|
|
<a href="spbu/index.php" class="app-card c-spbu">
|
|
<div class="card-head">
|
|
<div class="card-ico"><i class="fas fa-gas-pump"></i></div>
|
|
<span class="card-tag">Point Layer</span>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="card-name">Peta SPBU<br>Pontianak</div>
|
|
<div class="card-desc">
|
|
Sebaran lokasi SPBU Pertamina dengan informasi operasional,
|
|
nomor WhatsApp, status 24 jam, dan fitur geser marker.
|
|
</div>
|
|
</div>
|
|
<div class="card-stats">
|
|
<div>
|
|
<div class="cs-val"><?= sv($stats['spbu_total']) ?></div>
|
|
<div class="cs-lbl">Total SPBU</div>
|
|
</div>
|
|
<div>
|
|
<div class="cs-val"><?= sv($stats['spbu_24']) ?></div>
|
|
<div class="cs-lbl">Buka 24 Jam</div>
|
|
</div>
|
|
<div>
|
|
<div class="cs-val">WA</div>
|
|
<div class="cs-lbl">Kontak</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-arrow"><i class="fas fa-arrow-right"></i></div>
|
|
</a>
|
|
|
|
<!-- Kemiskinan -->
|
|
<a href="webgis-poverty-pontianak/index.php" class="app-card c-kemiskin">
|
|
<div class="card-head">
|
|
<div class="card-ico"><i class="fas fa-map-marked-alt"></i></div>
|
|
<span class="card-tag">Multi Layer</span>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="card-name">Pemetaan<br>Kemiskinan</div>
|
|
<div class="card-desc">
|
|
Sistem manajemen penduduk miskin, rumah ibadah sebagai pusat
|
|
bantuan, log distribusi bantuan, dan dashboard analitik.
|
|
</div>
|
|
</div>
|
|
<div class="card-stats">
|
|
<div>
|
|
<div class="cs-val"><?= sv($stats['kk_miskin']) ?></div>
|
|
<div class="cs-lbl">KK Miskin</div>
|
|
</div>
|
|
<div>
|
|
<div class="cs-val"><?= sv($stats['ibadah']) ?></div>
|
|
<div class="cs-lbl">Pusat Bantuan</div>
|
|
</div>
|
|
<div>
|
|
<div class="cs-val"><?= sv($stats['log_bantuan']) ?></div>
|
|
<div class="cs-lbl">Log Bantuan</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-arrow"><i class="fas fa-arrow-right"></i></div>
|
|
</a>
|
|
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<footer>
|
|
<div class="ft-l">WebGIS Pontianak © <?= $year ?> — Sistem Informasi Geospasial Kota</div>
|
|
<div class="ft-r">
|
|
<div class="ft-dot"></div>
|
|
<div class="ft-txt">Kalimantan Barat, Indonesia</div>
|
|
</div>
|
|
</footer>
|
|
|
|
</div><!-- /wrap -->
|
|
</body>
|
|
</html>
|