Files
manjapro_v6/dist/js/mapsTopologi.js
T
2026-08-20 18:39:37 +07:00

273 lines
9.0 KiB
JavaScript

/**
* MAPS TOPOLOGI - MAPLIBRE GEOJSON VERSION (High Performance)
* Menggunakan GeoJSON Source & Symbol Layer agar ringan untuk ribuan data.
*/
let map;
var currentStyle = 'hybrid';
// --- DEFINISI STYLE GOOGLE MAPS ---
const googleHybridStyle = {
'version': 8,
'sources': {
'google-hybrid': {
'type': 'raster',
'tiles': ['https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}'],
'tileSize': 256,
'attribution': '© Google Maps'
}
},
'layers': [{
'id': 'google-hybrid-layer',
'type': 'raster',
'source': 'google-hybrid',
'minzoom': 0,
'maxzoom': 22
}]
};
const googleStreetStyle = {
'version': 8,
'sources': {
'google-street': {
'type': 'raster',
'tiles': ['https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}'],
'tileSize': 256,
'attribution': '© Google Maps'
}
},
'layers': [{
'id': 'google-street-layer',
'type': 'raster',
'source': 'google-street',
'minzoom': 0,
'maxzoom': 22
}]
};
function initMap(center) {
// Init MapLibre
map = new maplibregl.Map({
container: 'map',
style: googleHybridStyle, // Default Hybrid
center: [center.lng, center.lat], // [Lng, Lat]
zoom: 10,
attributionControl: true,
dragRotate: false,
pitchWithRotate: false,
touchPitch: false
});
map.touchZoomRotate.disableRotation();
map.addControl(new maplibregl.NavigationControl({ showCompass: false, showZoom: true }), 'top-right');
map.addControl(new maplibregl.FullscreenControl(), 'top-left');
// Tambahkan tombol ganti layer
addLayerSwitcher();
// Tunggu map load selesai
map.on('load', function() {
// 1. Load Icon ke GPU (Wajib untuk GeoJSON)
loadIcons(map, () => {
// 2. Setup Source & Layer setelah icon siap
setupMapLayers();
// 3. Ambil data pertama kali
fetchMarkers();
// 4. Refresh marker tiap 30 detik
setInterval(fetchMarkers, 30000);
});
});
}
// --- FUNGSI BARU: Load Icon Gambar ke Memory Peta ---
function loadIcons(map, callback) {
// Daftar icon sesuai file yang ada di folder dist/img/
const icons = [
{ name: 'icon-odp', url: 'dist/img/ODP.png' },
{ name: 'icon-on', url: 'dist/img/ON.png' },
{ name: 'icon-off', url: 'dist/img/OF.png' },
{ name: 'icon-on1', url: 'dist/img/ON1.png' },
{ name: 'icon-off1', url: 'dist/img/OF1.png' }
];
let loadedCount = 0;
// Load satu per satu
icons.forEach(ic => {
map.loadImage(ic.url, (error, image) => {
if (error) {
console.error("Gagal load icon:", ic.url);
} else {
if (!map.hasImage(ic.name)) map.addImage(ic.name, image);
}
loadedCount++;
// Jika semua icon sudah diproses (sukses/gagal), jalankan callback
if (loadedCount === icons.length) {
if (callback) callback();
}
});
});
}
// --- FUNGSI BARU: Menyiapkan Wadah (Source & Layer) ---
function setupMapLayers() {
// Tambahkan Source Kosong (Wadah Data)
map.addSource('markers-source', {
type: 'geojson',
data: { type: 'FeatureCollection', features: [] }
});
// Tambahkan Layer Tampilan (Symbol Layer)
map.addLayer({
id: 'markers-layer',
type: 'symbol',
source: 'markers-source',
layout: {
'icon-image': ['get', 'iconName'], // Ambil nama icon dari properties GeoJSON
'icon-size': 1, // Sesuaikan ukuran (0.5 - 1.0)
'icon-allow-overlap': true,
'icon-anchor': 'bottom' // Ujung bawah icon menunjuk ke titik
}
});
// Event Klik untuk Popup
map.on('click', 'markers-layer', (e) => {
const coordinates = e.features[0].geometry.coordinates.slice();
const content = e.features[0].properties.content;
// Pastikan popup muncul di atas elemen lain
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
new maplibregl.Popup({ offset: 25 })
.setLngLat(coordinates)
.setHTML(content)
.addTo(map);
});
// Ubah kursor jadi tangan saat hover
map.on('mouseenter', 'markers-layer', () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'markers-layer', () => {
map.getCanvas().style.cursor = '';
});
}
function addLayerSwitcher() {
class LayerSwitchControl {
onAdd(map) {
this._map = map;
this._container = document.createElement('div');
this._container.className = 'maplibregl-ctrl maplibregl-ctrl-group';
const btn = document.createElement('button');
btn.innerHTML = '🗺️'; // Icon peta
btn.title = "Ganti Tampilan (Satelit/Jalan)";
btn.type = "button";
btn.onclick = () => {
if (currentStyle === 'hybrid') {
map.setStyle(googleStreetStyle);
currentStyle = 'street';
} else {
map.setStyle(googleHybridStyle);
currentStyle = 'hybrid';
}
// PENTING: Saat ganti style, resource gambar & source hilang.
// Kita harus load ulang icon dan layer.
setTimeout(() => {
loadIcons(map, () => {
// Cek apakah source masih ada, jika hilang buat lagi
if (!map.getSource('markers-source')) {
setupMapLayers();
fetchMarkers(); // Ambil data lagi
}
});
}, 500);
};
this._container.appendChild(btn);
return this._container;
}
onRemove() {
this._container.parentNode.removeChild(this._container);
this._map = undefined;
}
}
map.addControl(new LayerSwitchControl(), 'top-right');
}
function fetchMarkers() {
// topologiId diambil dari variabel global di halaman PHP
fetch(`load/maps_marker.php?i=${topologiId}`)
.then(response => {
if (!response.ok) throw new Error('Network response was not ok');
return response.json();
})
.then(res => {
if (!res.status) {
console.warn("⚠️ Tidak ada data marker");
return;
}
// --- KONVERSI DATA API KE GEOJSON ---
const features = res.data.map(item => {
// Tentukan nama icon berdasarkan filename dari database
let iconName = 'icon-odp'; // Default
// Cek string URL icon untuk menentukan jenisnya
if (item.icon.includes('ODP.png')) iconName = 'icon-odp';
else if (item.icon.includes('ON.png')) iconName = 'icon-on';
else if (item.icon.includes('OF.png')) iconName = 'icon-off';
else if (item.icon.includes('ON1.png')) iconName = 'icon-on1';
else if (item.icon.includes('OF1.png')) iconName = 'icon-off1';
return {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [parseFloat(item.lng), parseFloat(item.lat)]
},
properties: {
content: item.content, // HTML Popup
iconName: iconName // Nama icon untuk diload
}
};
});
// --- UPDATE DATA DI PETA (RINGAN) ---
const source = map.getSource('markers-source');
if (source) {
source.setData({
type: 'FeatureCollection',
features: features
});
console.log(`✅ ${features.length} Data updated via GeoJSON.`);
}
})
.catch(error => console.error('❌ Error fetching markers:', error));
}
// Entry Point: Ambil center dari API, lalu init map
window.onload = function() {
fetch(`load/maps_marker.php?i=${topologiId}`)
.then(res => res.json())
.then(res => {
if (res.status && res.centerlonglat) {
console.log("📍 Init map pakai centerlonglat:", res.centerlonglat);
initMap(res.centerlonglat);
} else {
console.warn("⚠️ Centerlonglat tidak ada, pakai fallback Jakarta");
initMap({ lat: -6.2, lng: 106.816666 });
}
})
.catch(err => {
console.error("❌ Gagal ambil data awal:", err);
initMap({ lat: -6.2, lng: 106.816666 }); // fallback
});
};