geolocation support
This commit is contained in:
		
							
								
								
									
										157
									
								
								app.js
									
									
									
									
									
								
							
							
						
						
									
										157
									
								
								app.js
									
									
									
									
									
								
							@ -58,12 +58,18 @@ let hotelMarker = null;
 | 
			
		||||
let routePolyline = null;
 | 
			
		||||
let hotelLocation = null;
 | 
			
		||||
let isPlacingHotel = false;
 | 
			
		||||
let userLocationMarker = null;
 | 
			
		||||
let userLocationCircle = null;
 | 
			
		||||
let gpsWatchId = null;
 | 
			
		||||
let hasInitialGpsFix = false;
 | 
			
		||||
let lastKnownLocation = null;
 | 
			
		||||
 | 
			
		||||
// Constants
 | 
			
		||||
const WALKING_SPEED_KMH = 5.0;
 | 
			
		||||
const WALKING_SPEED_MPH = 3.1;
 | 
			
		||||
const STREET_FACTOR = 1.3;
 | 
			
		||||
const BUDAPEST_CENTER = [47.4979, 19.0402];
 | 
			
		||||
const GPS_FOCUS_ZOOM = 15;
 | 
			
		||||
 | 
			
		||||
// Category colors for markers
 | 
			
		||||
const categoryColors = {
 | 
			
		||||
@ -109,6 +115,7 @@ const categoryIcons = {
 | 
			
		||||
 | 
			
		||||
// Initialize the application
 | 
			
		||||
document.addEventListener('DOMContentLoaded', function() {
 | 
			
		||||
  setGpsButtonState('idle');
 | 
			
		||||
  initializeMap();
 | 
			
		||||
  renderSightsList();
 | 
			
		||||
  setupEventListeners();
 | 
			
		||||
@ -185,6 +192,142 @@ function handleMapClick(e) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GPS button state manager
 | 
			
		||||
function setGpsButtonState(state) {
 | 
			
		||||
  const gpsButton = document.getElementById('gpsToggle');
 | 
			
		||||
  if (!gpsButton) return;
 | 
			
		||||
  
 | 
			
		||||
  gpsButton.classList.remove('gps-control--active', 'gps-control--pending');
 | 
			
		||||
  gpsButton.dataset.state = state;
 | 
			
		||||
  
 | 
			
		||||
  if (state === 'active') {
 | 
			
		||||
    gpsButton.classList.add('gps-control--active');
 | 
			
		||||
    gpsButton.setAttribute('aria-pressed', 'true');
 | 
			
		||||
    gpsButton.setAttribute('title', 'Center map on my location');
 | 
			
		||||
  } else if (state === 'pending') {
 | 
			
		||||
    gpsButton.classList.add('gps-control--pending');
 | 
			
		||||
    gpsButton.setAttribute('aria-pressed', 'true');
 | 
			
		||||
    gpsButton.setAttribute('title', 'Locating...');
 | 
			
		||||
  } else {
 | 
			
		||||
    gpsButton.setAttribute('aria-pressed', 'false');
 | 
			
		||||
    gpsButton.setAttribute('title', 'Show my location');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Handle GPS toggle clicks
 | 
			
		||||
function handleGpsButtonClick() {
 | 
			
		||||
  if (!gpsWatchId) {
 | 
			
		||||
    startGpsTracking();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  if (hasInitialGpsFix && lastKnownLocation) {
 | 
			
		||||
    map.setView(lastKnownLocation, Math.max(map.getZoom(), GPS_FOCUS_ZOOM), {
 | 
			
		||||
      animate: true
 | 
			
		||||
    });
 | 
			
		||||
  } else {
 | 
			
		||||
    setGpsButtonState('pending');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function startGpsTracking() {
 | 
			
		||||
  if (!('geolocation' in navigator)) {
 | 
			
		||||
    alert('Geolocation is not supported by your browser.');
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  setGpsButtonState('pending');
 | 
			
		||||
  
 | 
			
		||||
  gpsWatchId = navigator.geolocation.watchPosition(
 | 
			
		||||
    handleGpsSuccess,
 | 
			
		||||
    handleGpsError,
 | 
			
		||||
    {
 | 
			
		||||
      enableHighAccuracy: true,
 | 
			
		||||
      maximumAge: 10000,
 | 
			
		||||
      timeout: 15000
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleGpsSuccess(position) {
 | 
			
		||||
  const { latitude, longitude, accuracy } = position.coords;
 | 
			
		||||
  const latlng = [latitude, longitude];
 | 
			
		||||
  
 | 
			
		||||
  lastKnownLocation = latlng;
 | 
			
		||||
  
 | 
			
		||||
  if (!userLocationMarker) {
 | 
			
		||||
    userLocationMarker = L.circleMarker(latlng, {
 | 
			
		||||
      radius: 8,
 | 
			
		||||
      fillColor: '#2563eb',
 | 
			
		||||
      color: '#ffffff',
 | 
			
		||||
      weight: 2,
 | 
			
		||||
      opacity: 1,
 | 
			
		||||
      fillOpacity: 1,
 | 
			
		||||
      pane: 'markerPane'
 | 
			
		||||
    }).addTo(map);
 | 
			
		||||
  } else {
 | 
			
		||||
    userLocationMarker.setLatLng(latlng);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  if (!userLocationCircle) {
 | 
			
		||||
    userLocationCircle = L.circle(latlng, {
 | 
			
		||||
      radius: accuracy,
 | 
			
		||||
      color: '#2563eb',
 | 
			
		||||
      weight: 1,
 | 
			
		||||
      opacity: 0.6,
 | 
			
		||||
      fillColor: '#3b82f6',
 | 
			
		||||
      fillOpacity: 0.15
 | 
			
		||||
    }).addTo(map);
 | 
			
		||||
  } else {
 | 
			
		||||
    userLocationCircle.setLatLng(latlng);
 | 
			
		||||
    userLocationCircle.setRadius(accuracy);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  if (!hasInitialGpsFix) {
 | 
			
		||||
    hasInitialGpsFix = true;
 | 
			
		||||
    map.setView(latlng, Math.max(map.getZoom(), GPS_FOCUS_ZOOM));
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  setGpsButtonState('active');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleGpsError(error) {
 | 
			
		||||
  console.warn('Geolocation error:', error);
 | 
			
		||||
  
 | 
			
		||||
  if (error.code === error.PERMISSION_DENIED) {
 | 
			
		||||
    stopGpsTracking(true);
 | 
			
		||||
    alert('Location access was denied. Enable location permissions to display your position.');
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  if (!hasInitialGpsFix) {
 | 
			
		||||
    setGpsButtonState('idle');
 | 
			
		||||
    alert('Unable to determine your location. Please try again.');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function stopGpsTracking(clearLayers = false) {
 | 
			
		||||
  if (gpsWatchId !== null) {
 | 
			
		||||
    navigator.geolocation.clearWatch(gpsWatchId);
 | 
			
		||||
    gpsWatchId = null;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  if (clearLayers) {
 | 
			
		||||
    if (userLocationMarker) {
 | 
			
		||||
      map.removeLayer(userLocationMarker);
 | 
			
		||||
      userLocationMarker = null;
 | 
			
		||||
    }
 | 
			
		||||
    if (userLocationCircle) {
 | 
			
		||||
      map.removeLayer(userLocationCircle);
 | 
			
		||||
      userLocationCircle = null;
 | 
			
		||||
    }
 | 
			
		||||
    hasInitialGpsFix = false;
 | 
			
		||||
    lastKnownLocation = null;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  setGpsButtonState('idle');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Place hotel marker
 | 
			
		||||
function placeHotel(latlng) {
 | 
			
		||||
  // Remove existing hotel marker
 | 
			
		||||
@ -561,6 +704,12 @@ function setupEventListeners() {
 | 
			
		||||
  document.getElementById('clearRouteBtn').addEventListener('click', clearRoute);
 | 
			
		||||
  document.getElementById('downloadRouteBtn').addEventListener('click', downloadRoute);
 | 
			
		||||
  
 | 
			
		||||
  // GPS control
 | 
			
		||||
  const gpsButton = document.getElementById('gpsToggle');
 | 
			
		||||
  if (gpsButton) {
 | 
			
		||||
    gpsButton.addEventListener('click', handleGpsButtonClick);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  // Modal controls
 | 
			
		||||
  document.getElementById('closeModal').addEventListener('click', () => {
 | 
			
		||||
    document.getElementById('sightModal').classList.add('hidden');
 | 
			
		||||
@ -578,6 +727,12 @@ function setupEventListeners() {
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
window.addEventListener('beforeunload', () => {
 | 
			
		||||
  if (gpsWatchId !== null && 'geolocation' in navigator) {
 | 
			
		||||
    navigator.geolocation.clearWatch(gpsWatchId);
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Clear route
 | 
			
		||||
function clearRoute() {
 | 
			
		||||
  selectedSights.clear();
 | 
			
		||||
@ -665,4 +820,4 @@ function showInstructions() {
 | 
			
		||||
 | 
			
		||||
// Make functions available globally for HTML onclick handlers
 | 
			
		||||
window.toggleSightSelection = toggleSightSelection;
 | 
			
		||||
window.openSightModal = openSightModal;
 | 
			
		||||
window.openSightModal = openSightModal;
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user