1. Latar Belakang
Dalam pengembangan portal informasi kota, peta tidak cukup hanya menampilkan marker secara statis. Dibutuhkan pengelolaan data yang terstruktur, fitur pencarian lokasi, serta tampilan yang informatif agar pengguna lebih mudah memahami informasi yang ditampilkan. Oleh karena itu, pada tahap ini dilakukan pengembangan fitur peta interaktif yang mencakup pengelompokan data, navigasi berbasis parameter URL, serta penambahan elemen visual seperti header dan legend.
2. Alat dan Bahan
- Visual Studio Code (atau text editor lainnya)
- Laravel
- React (opsional)
- Library Leaflet
B. Perangkat Keras
- Laptop / PC
3. Pembahasan
3.1 Pengelompokan Data Lokasi
Data lokasi dalam peta tidak ditulis secara
manual satu per satu, tetapi diambil dari sumber data terstruktur. Disini,
saya membuat data dummy lewat JavaScript yang saya taruh di sebuah folder
tersendiri, yaitu:
SMARTCITY/
│
├── backend/
│
├── frontend/
│ ├── src/
│ │ ├── assets/
│ │ ├── components/
│ │ ├── data/
│ │ │ └── mockData.js (Tempat
data dummy)
│ │ ├── assets/
│ │ └── pages/
│ ├── App.css
│ ├── App.jsx
│ ├── index.css
│ ├── main.jsx
│ └── package.json
Didalamnya, terdapat data-data dummy dari
suatu lokasi atau tempat seperti data nama wisata, data gambar, dan lain
sebagainya seperti yang dapat dilihat di salah satu bentuk data dibawah:
export const wisataData = [
{
id: 1,
nama: 'Owabong Water Park',
slug: 'owabong-water-park',
kategori: 'Wisata Air',
gambar: 'https://images.unsplash.com/photo-1559827260-dc66d52bef19?w=600&q=80',
lokasi: 'Kec. Bojongsari, Purbalingga',
kecamatan: 'Bojongsari',
deskripsi: 'Objek wisata air terpopuler di Purbalingga dengan berbagai wahana seru dan kolam renang modern yang menjadi favorit keluarga.',
deskripsi_panjang: 'Owabong (Obyek Wisata Air Bojongsari) adalah taman air terbesar dan terpopuler di Kabupaten Purbalingga. Dengan luas area lebih dari 5 hektar, Owabong menawarkan berbagai wahana air yang seru dan aman untuk seluruh keluarga.',
harga_dewasa: 50000,
harga_anak: 35000,
rating: 4.5,
ulasan: 2847,
jam_buka: '08:00 – 17:00 WIB',
fasilitas: 'Parkir, Mushola, Food Court',
kontak: '(0281) 892234',
lat: -7.3642,
lng: 109.3456,
},
3.2 Pengembangan Fitur Pencarian Data Lokasi
Fitur pencarian diimplementasikan menggunakan parameter URL. Ketika user memilih lokasi, maka halaman akan menerima koordinat dan nama lokasi tersebut.
Contoh URL : /peta?nama=Owabong&lat=-7.388&lng=109.363
if (instanceRef.current) {
if (hasFly) instanceRef.current.flyTo([flyLat, flyLng], ZOOM_WISATA, { duration: 1.2 });
return;
}
3.3 Penggunaan Layer pada Peta
Untuk meningkatkan tampilan visual, digunakan tile layer dari CARTO (light mode) untuk tampilan map yang lebih bersih dan bagus:
L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
attribution: '© CARTO',
maxZoom: 19,
}).addTo(map);
Hasil:
3.4 Navigasi Header Peta
Navigasi pada bagian atas peta berfungsi sebagai identitas halaman sekaligus memberikan akses cepat untuk kembali ke halaman sebelumnya (Portal Informasi). Tampilan ini dibuat menggunakan elemen HTML biasa yang diposisikan di atas peta atau biasa disebut sebagai overlay.
<div style={{
position: 'absolute', top: 16, left: '50%', transform: 'translateX(-50%)',
zIndex: 1000, background: 'rgba(10,29,61,.85)', backdropFilter: 'blur(12px)',
border: '1px solid rgba(255,255,255,.12)', borderRadius: 50,
padding: '10px 24px', display: 'flex', alignItems: 'center', gap: 14,
color: 'white', boxShadow: '0 4px 20px rgba(0,0,0,.4)',
}}>
<Link to="/" style={{ display: 'flex', alignItems: 'center', gap: 8, color: 'rgba(255,255,255,.7)', textDecoration: 'none', fontSize: 13, transition: 'color .2s' }}>
<i className="fas fa-arrow-left" /> Kembali
</Link>
<div style={{ width: 1, height: 20, background: 'rgba(255,255,255,.2)' }} />
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<i className="fas fa-map-marked-alt" style={{ color: '#5eead4' }} />
<span style={{ fontWeight: 700, fontSize: 14 }}>Peta Wisata Purbalingga</span>
</div>
{hasFly && (
<>
<div style={{ width: 1, height: 20, background: 'rgba(255,255,255,.2)' }} />
<div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 13, color: '#d4a853' }}>
<i className="fas fa-location-dot" /> {flyNama}
</div>
</>
)}
</div>
Hasil:3.5 Tampilan Legend (Keterangan Peta)
Legend digunakan untuk menjelaskan arti dari setiap simbol atau marker yang ada pada peta, sehingga pengguna tidak kebingungan dalam membaca informasi.
<div style={{
position: 'absolute', bottom: 24, left: 16, zIndex: 1000,
background: 'rgba(10,29,61,.85)', backdropFilter: 'blur(10px)',
border: '1px solid rgba(255,255,255,.12)', borderRadius: 12,
padding: '14px 18px', color: 'white', minWidth: 200,
boxShadow: '0 4px 20px rgba(0,0,0,.4)',
}}>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: 2, textTransform: 'uppercase', color: '#5eead4', marginBottom: 10 }}>
Legend
</div>
{[
{ color: '#0d9488', label: 'Destinasi Wisata' },
{ color: '#d4a853', label: 'Destinasi Pilihan' },
{ color: '#5d93c7', label: 'Pusat Kota' },
].map((l) => (
<div key={l.label} style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 7 }}>
<div style={{ width: 12, height: 12, borderRadius: '50%', background: l.color, border: '2px solid white', flexShrink: 0 }} />
<span style={{ fontSize: 12, color: 'rgba(255,255,255,.8)' }}>{l.label}</span>
</div>
))}
<div style={{ marginTop: 12, paddingTop: 10, borderTop: '1px solid rgba(255,255,255,.12)', fontSize: 11, color: 'rgba(255,255,255,.45)' }}>
{wisataData.length} destinasi wisata terdaftar
</div>
</div>Hasil:3.6 Fitur Pencarian Tempat
Fitur ini dapat memudahkan pengguna untuk mencari letak dan informasi dari suatu tempat yang ingin dicari.
<div style={{
position: 'absolute', top: 80, right: 16, zIndex: 1000,
background: 'rgba(10,29,61,.85)', backdropFilter: 'blur(10px)',
border: '1px solid rgba(255,255,255,.12)', borderRadius: 12,
color: 'white', width: 240,
boxShadow: '0 4px 20px rgba(0,0,0,.4)', overflow: 'hidden',
maxHeight: 'calc(100vh - 120px)', display: 'flex', flexDirection: 'column',
}}>
{/* Header */}
<div style={{ padding: '12px 16px', borderBottom: '1px solid rgba(255,255,255,.1)', fontSize: 11, fontWeight: 700, letterSpacing: 2, textTransform: 'uppercase', color: '#5eead4', flexShrink: 0 }}>
Destinasi Wisata
</div>
{/* Search input */}
<div style={{ padding: '10px 12px', borderBottom: '1px solid rgba(255,255,255,.1)', flexShrink: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, background: 'rgba(255,255,255,.08)', borderRadius: 20, padding: '7px 12px' }}>
<i className="fas fa-search" style={{ color: '#5eead4', fontSize: 11 }} />
<input
type="text"
placeholder="Cari destinasi..."
value={query}
onChange={e => setQuery(e.target.value)}
style={{
background: 'none', border: 'none', outline: 'none',
color: 'white', fontSize: 12, width: '100%',
}}
/>
{query && (
<button onClick={() => setQuery('')} style={{ background: 'none', border: 'none', color: 'rgba(255,255,255,.4)', cursor: 'pointer', padding: 0, fontSize: 12, lineHeight: 1 }}>
✕
</button>
)}
</div>
</div>
{/* List hasil filter */}
<div style={{ overflowY: 'auto', flex: 1 }}>
{filteredWisata.length === 0 ? (
<div style={{ padding: 20, textAlign: 'center', color: 'rgba(255,255,255,.4)', fontSize: 12 }}>
Tidak ditemukan
</div>
) : filteredWisata.map((w) => {
const isActive = hasFly && w.nama === flyNama;
return (
<a key={w.id}
href={`/peta?nama=${encodeURIComponent(w.nama)}&lat=${w.lat}&lng=${w.lng}`}
style={{
display: 'flex', alignItems: 'center', gap: 10, padding: '10px 14px',
background: isActive ? 'rgba(13,148,136,.25)' : 'transparent',
borderLeft: isActive ? '3px solid #0d9488' : '3px solid transparent',
textDecoration: 'none', transition: 'background .15s',
borderBottom: '1px solid rgba(255,255,255,.06)',
}}
onMouseEnter={e => { if (!isActive) e.currentTarget.style.background = 'rgba(255,255,255,.07)'; }}
onMouseLeave={e => { if (!isActive) e.currentTarget.style.background = 'transparent'; }}>
<div style={{ width: 8, height: 8, borderRadius: '50%', background: isActive ? '#d4a853' : '#0d9488', flexShrink: 0 }} />
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ fontSize: 13, fontWeight: isActive ? 700 : 500, color: isActive ? '#d4a853' : 'white', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{w.nama}
</div>
<div style={{ fontSize: 11, color: 'rgba(255,255,255,.45)' }}>{w.kecamatan}</div>
</div>
</a>
);
})}
</div>
</div>
Hasil:
4. Daftar Pustaka
Leaflet. (n.d.). What are panes?. Diakses dari: https://leafletjs.com/examples/map-panes/
.png)
0 Comments