279 lines
9.4 KiB
JavaScript
279 lines
9.4 KiB
JavaScript
/**
|
|
* Admin Map Management Module
|
|
* Handles map initialization, start location management, and coordinate operations
|
|
*/
|
|
|
|
// Map state
|
|
let adminMap = null;
|
|
let startMarker = null;
|
|
|
|
// Initialize the admin map
|
|
function initializeAdminMap() {
|
|
// Check if the map container is visible before initializing
|
|
const mapContainer = document.getElementById('admin-map');
|
|
if (!mapContainer) {
|
|
console.warn('Admin map container not found, delaying initialization');
|
|
return;
|
|
}
|
|
|
|
// If container is hidden, delay initialization
|
|
const isVisible = mapContainer.offsetWidth > 0 && mapContainer.offsetHeight > 0;
|
|
if (!isVisible) {
|
|
console.log('Admin map container is hidden, delaying initialization until section is shown');
|
|
return;
|
|
}
|
|
|
|
// Avoid double initialization
|
|
if (adminMap) {
|
|
console.log('Admin map already initialized, invalidating size instead');
|
|
adminMap.invalidateSize();
|
|
return;
|
|
}
|
|
|
|
console.log('Initializing admin map...');
|
|
adminMap = L.map('admin-map').setView([53.5461, -113.4938], 11);
|
|
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
|
maxZoom: 19,
|
|
minZoom: 2
|
|
}).addTo(adminMap);
|
|
|
|
// Add crosshair to center of map
|
|
const crosshairIcon = L.divIcon({
|
|
className: 'crosshair',
|
|
iconSize: [20, 20],
|
|
html: '<div style="width: 20px; height: 20px; position: relative;"><div style="position: absolute; top: 9px; left: 0; width: 20px; height: 2px; background: #333; box-shadow: 0 0 3px rgba(255,255,255,0.8);"></div><div style="position: absolute; top: 0; left: 9px; width: 2px; height: 20px; background: #333; box-shadow: 0 0 3px rgba(255,255,255,0.8);"></div></div>'
|
|
});
|
|
|
|
const crosshair = L.marker(adminMap.getCenter(), {
|
|
icon: crosshairIcon,
|
|
interactive: false,
|
|
zIndexOffset: 1000
|
|
}).addTo(adminMap);
|
|
|
|
// Update crosshair position when map moves
|
|
adminMap.on('move', function() {
|
|
crosshair.setLatLng(adminMap.getCenter());
|
|
});
|
|
|
|
// Add click handler to set location
|
|
adminMap.on('click', handleMapClick);
|
|
|
|
// Update coordinates when map moves
|
|
adminMap.on('moveend', updateCoordinatesFromMap);
|
|
|
|
// Trigger size invalidation after a brief moment to ensure proper rendering
|
|
setTimeout(() => {
|
|
if (adminMap) {
|
|
adminMap.invalidateSize();
|
|
}
|
|
}, 100);
|
|
}
|
|
|
|
// Load current start location
|
|
async function loadCurrentStartLocation() {
|
|
try {
|
|
const response = await fetch('/api/admin/start-location');
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
const { latitude, longitude, zoom } = data.location;
|
|
|
|
// Update form fields
|
|
const latInput = document.getElementById('start-lat');
|
|
const lngInput = document.getElementById('start-lng');
|
|
const zoomInput = document.getElementById('start-zoom');
|
|
|
|
if (latInput) latInput.value = latitude;
|
|
if (lngInput) lngInput.value = longitude;
|
|
if (zoomInput) zoomInput.value = zoom;
|
|
|
|
// Update map
|
|
if (adminMap) {
|
|
adminMap.setView([latitude, longitude], zoom);
|
|
updateStartMarker(latitude, longitude);
|
|
}
|
|
|
|
// Show source info
|
|
if (data.source) {
|
|
const sourceText = data.source === 'database' ? 'Loaded from database' :
|
|
data.source === 'environment' ? 'Using environment defaults' :
|
|
'Using system defaults';
|
|
window.adminCore.showStatus(sourceText, 'info');
|
|
}
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Failed to load start location:', error);
|
|
window.adminCore.showStatus('Failed to load current start location', 'error');
|
|
}
|
|
}
|
|
|
|
// Handle map click
|
|
function handleMapClick(e) {
|
|
const { lat, lng } = e.latlng;
|
|
|
|
const latInput = document.getElementById('start-lat');
|
|
const lngInput = document.getElementById('start-lng');
|
|
|
|
if (latInput) latInput.value = lat.toFixed(6);
|
|
if (lngInput) lngInput.value = lng.toFixed(6);
|
|
|
|
updateStartMarker(lat, lng);
|
|
}
|
|
|
|
// Update marker position
|
|
function updateStartMarker(lat, lng) {
|
|
if (startMarker) {
|
|
startMarker.setLatLng([lat, lng]);
|
|
} else {
|
|
startMarker = L.marker([lat, lng], {
|
|
draggable: true,
|
|
title: 'Start Location'
|
|
}).addTo(adminMap);
|
|
|
|
// Update coordinates when marker is dragged
|
|
startMarker.on('dragend', (e) => {
|
|
const position = e.target.getLatLng();
|
|
const latInput = document.getElementById('start-lat');
|
|
const lngInput = document.getElementById('start-lng');
|
|
|
|
if (latInput) latInput.value = position.lat.toFixed(6);
|
|
if (lngInput) lngInput.value = position.lng.toFixed(6);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Update coordinates from current map view
|
|
function updateCoordinatesFromMap() {
|
|
const center = adminMap.getCenter();
|
|
const zoom = adminMap.getZoom();
|
|
|
|
const zoomInput = document.getElementById('start-zoom');
|
|
if (zoomInput) {
|
|
zoomInput.value = zoom;
|
|
}
|
|
}
|
|
|
|
// Update map from input fields
|
|
function updateMapFromInputs() {
|
|
const latInput = document.getElementById('start-lat');
|
|
const lngInput = document.getElementById('start-lng');
|
|
const zoomInput = document.getElementById('start-zoom');
|
|
|
|
const lat = parseFloat(latInput?.value);
|
|
const lng = parseFloat(lngInput?.value);
|
|
const zoom = parseInt(zoomInput?.value);
|
|
|
|
if (!isNaN(lat) && !isNaN(lng) && !isNaN(zoom) && adminMap) {
|
|
adminMap.setView([lat, lng], zoom);
|
|
updateStartMarker(lat, lng);
|
|
}
|
|
}
|
|
|
|
// Save start location
|
|
async function saveStartLocation() {
|
|
const latInput = document.getElementById('start-lat');
|
|
const lngInput = document.getElementById('start-lng');
|
|
const zoomInput = document.getElementById('start-zoom');
|
|
|
|
const lat = parseFloat(latInput?.value);
|
|
const lng = parseFloat(lngInput?.value);
|
|
const zoom = parseInt(zoomInput?.value);
|
|
|
|
// Validate
|
|
if (isNaN(lat) || isNaN(lng) || isNaN(zoom)) {
|
|
window.adminCore.showStatus('Please enter valid coordinates and zoom level', 'error');
|
|
return;
|
|
}
|
|
|
|
if (lat < -90 || lat > 90 || lng < -180 || lng > 180) {
|
|
window.adminCore.showStatus('Coordinates out of valid range', 'error');
|
|
return;
|
|
}
|
|
|
|
if (zoom < 2 || zoom > 19) {
|
|
window.adminCore.showStatus('Zoom level must be between 2 and 19', 'error');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch('/api/admin/start-location', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
latitude: lat,
|
|
longitude: lng,
|
|
zoom: zoom
|
|
})
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
window.adminCore.showStatus('Start location saved successfully!', 'success');
|
|
} else {
|
|
throw new Error(data.error || 'Failed to save');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Save error:', error);
|
|
window.adminCore.showStatus(error.message || 'Failed to save start location', 'error');
|
|
}
|
|
}
|
|
|
|
// Setup map-related event listeners
|
|
function setupMapEventListeners() {
|
|
// Use current view button
|
|
const useCurrentViewBtn = document.getElementById('use-current-view');
|
|
if (useCurrentViewBtn) {
|
|
useCurrentViewBtn.addEventListener('click', () => {
|
|
if (!adminMap) return;
|
|
|
|
const center = adminMap.getCenter();
|
|
const zoom = adminMap.getZoom();
|
|
|
|
const latInput = document.getElementById('start-lat');
|
|
const lngInput = document.getElementById('start-lng');
|
|
const zoomInput = document.getElementById('start-zoom');
|
|
|
|
if (latInput) latInput.value = center.lat.toFixed(6);
|
|
if (lngInput) lngInput.value = center.lng.toFixed(6);
|
|
if (zoomInput) zoomInput.value = zoom;
|
|
|
|
updateStartMarker(center.lat, center.lng);
|
|
window.adminCore.showStatus('Captured current map view', 'success');
|
|
});
|
|
}
|
|
|
|
// Save button
|
|
const saveLocationBtn = document.getElementById('save-start-location');
|
|
if (saveLocationBtn) {
|
|
saveLocationBtn.addEventListener('click', saveStartLocation);
|
|
}
|
|
|
|
// Coordinate input changes
|
|
const startLatInput = document.getElementById('start-lat');
|
|
const startLngInput = document.getElementById('start-lng');
|
|
const startZoomInput = document.getElementById('start-zoom');
|
|
|
|
if (startLatInput) startLatInput.addEventListener('change', updateMapFromInputs);
|
|
if (startLngInput) startLngInput.addEventListener('change', updateMapFromInputs);
|
|
if (startZoomInput) startZoomInput.addEventListener('change', updateMapFromInputs);
|
|
}
|
|
|
|
// Export map management functions
|
|
window.adminMap = {
|
|
initializeAdminMap,
|
|
loadCurrentStartLocation,
|
|
setupMapEventListeners,
|
|
saveStartLocation,
|
|
updateMapFromInputs,
|
|
handleMapClick,
|
|
updateStartMarker,
|
|
getAdminMap: () => adminMap
|
|
};
|