`,
iconSize: [40, 50],
iconAnchor: [20, 40],
popupAnchor: [0, -25]
});
}
// Fungsi update RX Power untuk marker tertentu
// map.js//modifan saya
// pastikan variabel sudah benar
// update RX Power setelah marker dirender
// Create popup content for item
function createPopupContent(item) {
let tubeColorName = item.tube_color_name || 'Tidak ada';
let splitterMain = item.splitter_main_ratio || 'Tidak ada';
let splitterOdp = item.splitter_odp_ratio || 'Tidak ada';
let serialNumber = item.serial_number ? item.serial_number.trim() : '';
let html = `
${item.name}
Jenis: ${item.item_type_name}
${item.description ? `
Deskripsi: ${item.description}
` : ''}
${item.address ? `
Alamat: ${item.address}
` : ''}
Warna Tube: ${tubeColorName}
${item.core_used ? `
Core Digunakan: ${item.core_used}
` : ''}
Splitter Utama: ${splitterMain}
Splitter ODP: ${splitterOdp}
Status:${getStatusText(item.status)}
`;
// Ambil RX Power setelah popup ada di DOM
// Lebih baik menggunakan requestAnimationFrame untuk memastikan DOM sudah diupdate
return html;
}
// Get item icon based on type (moved to bottom for global export)
// Get status badge class
function getStatusBadgeClass(status) {
switch(status) {
case 'active': return 'success';
case 'inactive': return 'secondary';
case 'maintenance': return 'warning';
default: return 'primary';
}
}
// Get status text
function getStatusText(status) {
switch(status) {
case 'active': return 'Aktif';
case 'inactive': return 'Tidak Aktif';
case 'maintenance': return 'Maintenance';
default: return status;
}
}
// Load items from database
function loadItems() {
$.ajax({
url: 'api/items.php',
method: 'GET',
success: function(response) {
if (response.success) {
response.data.forEach(function(item) {
addMarkerToMap(item);
});
updateStatistics();
}
},
error: function() {
showNotification('Error loading items', 'error');
}
});
}
// Add marker to map
function addMarkerToMap(item) {
let itemTypeColors = {
'OLT': '#FF6B6B',
'Tiang Tumpu': '#4ECDC4',
'ODP': '#45B7D1',
'ODC': '#96CEB4',
'Pelanggan': '#FFA500'
};
let color = itemTypeColors[item.item_type_name] || '#999';
let icon = createCustomIcon(item.item_type_name, color);
let marker = L.marker([item.latitude, item.longitude], {
icon: icon,
draggable: true,
itemType: item.item_type_name,
itemId: item.id,
itemData: item
}).addTo(map);
marker.bindPopup(createPopupContent(item));
// Add drag event
marker.on('dragend', function(e) {
let newPos = e.target.getLatLng();
updateItemPosition(item.id, newPos.lat, newPos.lng);
});
// Add click event for routing mode
marker.on('click', function(e) {
if (isRoutingMode) {
handleRoutingClick(item);
e.originalEvent.stopPropagation();
}
});
markers[item.id] = marker;
}
// Update item position
function updateItemPosition(itemId, lat, lng) {
// Use FormData with method override for consistency
let formData = new FormData();
formData.append('_method', 'PUT');
formData.append('id', itemId);
formData.append('latitude', lat);
formData.append('longitude', lng);
$.ajax({
url: 'api/items.php',
method: 'POST',
data: formData,
processData: false,
contentType: false,
dataType: 'json',
success: function(response) {
if (response && response.success) {
showNotification('Posisi item berhasil dipindahkan', 'success');
} else {
showNotification(response?.message || 'Error updating position', 'error');
}
},
error: function(xhr, status, error) {
console.error('Position update error:', error, xhr.responseText);
showNotification('Error updating position: ' + error, 'error');
}
});
}
// Start routing mode
function startRouting(itemId) {
isRoutingMode = true;
routingFromItem = itemId;
map.getContainer().style.cursor = 'crosshair';
showNotification('Pilih item tujuan untuk membuat route', 'info');
}
// Handle routing click
function handleRoutingClick(toItem) {
if (routingFromItem && routingFromItem !== toItem.id) {
createRoute(routingFromItem, toItem.id);
exitRoutingMode();
}
}
// Exit routing mode
function exitRoutingMode() {
isRoutingMode = false;
routingFromItem = null;
map.getContainer().style.cursor = '';
}
// Create route between two items
function createRoute(fromItemId, toItemId) {
let fromMarker = markers[fromItemId];
let toMarker = markers[toItemId];
if (!fromMarker || !toMarker) {
showNotification('Marker tidak ditemukan', 'error');
return;
}
let fromPos = fromMarker.getLatLng();
let toPos = toMarker.getLatLng();
console.log('Creating route from', fromPos, 'to', toPos);
// Check if Leaflet Routing Machine is available
if (typeof L.Routing === 'undefined') {
console.log('Leaflet Routing Machine not available, creating simple line');
// Create simple straight line if routing machine not available
createSimpleRoute(fromItemId, toItemId, fromPos, toPos);
return;
}
try {
// Use routing machine to create route following roads
let routing = L.Routing.control({
waypoints: [fromPos, toPos],
routeWhileDragging: false,
show: false,
createMarker: function() { return null; }, // Don't create default markers
addWaypoints: false,
draggableWaypoints: false,
fitSelectedRoutes: false
});
routing.on('routesfound', function(e) {
console.log('Route found:', e.routes[0]);
let route = e.routes[0];
let coordinates = route.coordinates;
// Save route to database
$.ajax({
url: 'api/routes.php',
method: 'POST',
data: {
from_item_id: fromItemId,
to_item_id: toItemId,
route_coordinates: JSON.stringify(coordinates),
distance: route.summary.totalDistance,
cable_type: 'Fiber Optic',
core_count: 24,
status: 'planned'
},
success: function(response) {
console.log('Route save response:', response);
if (response.success) {
// Add route line to map
let routeLine = L.polyline(coordinates, {
color: '#ffc107',
weight: 4,
opacity: 0.8,
dashArray: '10, 5'
}).addTo(map);
// Add popup to route
routeLine.bindPopup(`
Route Kabel
Jarak: ${(route.summary.totalDistance / 1000).toFixed(2)} km
`);
routes[response.route_id] = routeLine;
showNotification('Route sederhana berhasil dibuat', 'success');
}
},
error: function() {
showNotification('Error menyimpan route', 'error');
}
});
}
// Load routes from database
function loadRoutes() {
$.ajax({
url: 'api/routes.php',
method: 'GET',
success: function(response) {
if (response.success) {
response.data.forEach(function(route) {
if (route.route_coordinates) {
let coordinates = JSON.parse(route.route_coordinates);
let color = getRouteColor(route.status);
let dashArray = route.status === 'installed' ? null : '10, 5';
let routeLine = L.polyline(coordinates, {
color: color,
weight: 4,
opacity: 0.8,
dashArray: dashArray
}).addTo(map);
routeLine.bindPopup(`
Route Kabel
Jarak: ${(route.distance / 1000).toFixed(2)} km
Tipe Kabel: ${route.cable_type}
Jumlah Core: ${route.core_count}
Status: ${getStatusText(route.status)}
`);
routes[route.id] = routeLine;
}
});
}
}
});
}
// Get route color based on status
function getRouteColor(status) {
switch(status) {
case 'installed': return '#28a745';
case 'planned': return '#ffc107';
case 'maintenance': return '#dc3545';
default: return '#6c757d';
}
}
// Add map legend
function addMapLegend() {
let legend = L.control({position: 'bottomleft'});
legend.onAdd = function(map) {
let div = L.DomUtil.create('div', 'map-legend');
div.innerHTML = `
Legend
OLT
Tiang Tumpu
ODP
ODC
Pelanggan
━━━ Terpasang
┅┅┅ Perencanaan
┅┅┅ Maintenance
`;
return div;
};
legend.addTo(map);
}
//buatan saya
// Update statistics
function updateStatistics() {
$.ajax({
url: 'api/statistics.php',
method: 'GET',
success: function(response) {
if (response.success) {
$('#stat-olt').text(response.data.olt || 0);
$('#stat-tiang').text(response.data.tiang_tumpu || 0);
$('#stat-odp').text(response.data.odp || 0);
$('#stat-odc').text(response.data.odc || 0);
$('#stat-pelanggan').text(response.data.pelanggan || 0);
$('#stat-routes').text(response.data.total_routes || 0);
}
}
});
}
// Show notification
function showNotification(message, type) {
let alertClass = 'alert-info';
switch(type) {
case 'success': alertClass = 'alert-success'; break;
case 'error': alertClass = 'alert-danger'; break;
case 'warning': alertClass = 'alert-warning'; break;
}
let notification = `
${message}
`;
$('body').append(notification);
setTimeout(function() {
$('.alert').fadeOut();
}, 5000);
}
// Show routing mode
function showRoutingMode() {
if (isRoutingMode) {
exitRoutingMode();
showNotification('Mode routing dinonaktifkan', 'info');
} else {
isRoutingMode = true;
map.getContainer().style.cursor = 'crosshair';
showNotification('Mode routing aktif. Klik dua item untuk membuat route.', 'info');
}
}
// Zoom to specific bounds
function zoomToItems() {
if (Object.keys(markers).length > 0) {
const group = new L.featureGroup(Object.values(markers));
map.fitBounds(group.getBounds().pad(0.1));
} else {
showNotification('Tidak ada item untuk di-zoom', 'warning');
}
}
// Zoom to specific item type
function zoomToItemType(itemType) {
const filteredMarkers = Object.values(markers).filter(marker => {
return marker.options && marker.options.itemType === itemType;
});
if (filteredMarkers.length > 0) {
const group = new L.featureGroup(filteredMarkers);
map.fitBounds(group.getBounds().pad(0.1));
// Highlight markers of this type temporarily
filteredMarkers.forEach(marker => {
if (marker._icon) {
marker._icon.style.transform += ' scale(1.3)';
marker._icon.style.zIndex = '1000';
setTimeout(() => {
marker._icon.style.transform = marker._icon.style.transform.replace(' scale(1.3)', '');
marker._icon.style.zIndex = '';
}, 2000);
}
});
showNotification(`Menampilkan ${filteredMarkers.length} ${itemType}`, 'success');
} else {
showNotification(`Tidak ada ${itemType} ditemukan`, 'info');
}
}
// Enhanced locate user function
function locateUser() {
if (navigator.geolocation) {
map.locate({
setView: true,
maxZoom: 16,
enableHighAccuracy: true,
timeout: 10000
});
map.on('locationfound', function(e) {
L.circle(e.latlng, e.accuracy).addTo(map)
.bindPopup('Anda berada di sekitar area ini').openPopup();
showNotification('Lokasi berhasil ditemukan', 'success');
});
map.on('locationerror', function(e) {
showNotification('Gagal menemukan lokasi: ' + e.message, 'error');
});
} else {
showNotification('Geolocation tidak didukung browser ini', 'error');
}
}
// Add keyboard shortcuts for zoom
function addKeyboardShortcuts() {
document.addEventListener('keydown', function(e) {
if (e.target.tagName.toLowerCase() === 'input' || e.target.tagName.toLowerCase() === 'textarea') {
return; // Don't interfere with form inputs
}
switch(e.key) {
case '+':
case '=':
map.zoomIn();
break;
case '-':
map.zoomOut();
break;
case 'h':
case 'H':
map.setView([-2.5, 118], 5); // Home to Indonesia
break;
case 'f':
case 'F':
if (map.isFullscreen && map.isFullscreen()) {
map.toggleFullscreen();
} else if (map.toggleFullscreen) {
map.toggleFullscreen();
}
break;
case 'l':
case 'L':
locateUser();
break;
case 'a':
case 'A':
zoomToItems();
break;
}
});
}
// Enhanced map ready function
function onMapReady() {
addKeyboardShortcuts();
// Add help tooltip
const helpControl = L.control({position: 'bottomright'});
helpControl.onAdd = function(map) {
const div = L.DomUtil.create('div', 'leaflet-control-help');
div.innerHTML = '';
div.style.background = 'rgba(255,255,255,0.8)';
div.style.padding = '5px';
div.style.borderRadius = '3px';
div.style.cursor = 'help';
return div;
};
helpControl.addTo(map);
console.log('🎮 Map keyboard shortcuts enabled: +/- zoom, H home, F fullscreen, L locate, A zoom to all');
}
// Helper functions needed by detail modal
function getItemIcon(typeName) {
switch(typeName) {
case 'OLT': return 'fas fa-server';
case 'Tiang Tumpu': return 'fas fa-tower-broadcast';
case 'ODP': return 'fas fa-project-diagram';
case 'ODC': return 'fas fa-network-wired';
case 'Pelanggan': return 'fas fa-home';
default: return 'fas fa-circle';
}
}
function getStatusBadgeClass(status) {
switch(status) {
case 'active': return 'success';
case 'inactive': return 'secondary';
case 'maintenance': return 'warning';
default: return 'secondary';
}
}
function getStatusText(status) {
switch(status) {
case 'active': return 'Aktif';
case 'inactive': return 'Tidak Aktif';
case 'maintenance': return 'Maintenance';
default: return status || 'Unknown';
}
}
// Export functions to global scope for button access
window.zoomToItems = zoomToItems;
window.zoomToItemType = zoomToItemType;
window.locateUser = locateUser;
window.loadRoutes = loadRoutes;
window.getItemIcon = getItemIcon;
window.getStatusBadgeClass = getStatusBadgeClass;
window.getStatusText = getStatusText;
// Initialize map when document is ready
$(document).ready(function() {
initMap();
setTimeout(onMapReady, 1000); // Wait for map to fully initialize
});