Files
moto-adv-website/app/static/map_iframe.html

364 lines
14 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Adventure Routes Map</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
overflow: hidden;
}
#map {
height: 100vh;
width: 100vw;
}
.map-loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1000;
background: rgba(255, 255, 255, 0.95);
padding: 20px;
border-radius: 8px;
text-align: center;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.spinner {
border: 3px solid #f3f3f3;
border-top: 3px solid #f97316;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin: 0 auto 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.route-popup {
min-width: 250px;
font-family: inherit;
}
.route-popup h3 {
margin: 0 0 10px 0;
color: #1f2937;
font-size: 1.1em;
font-weight: 600;
}
.route-popup .stat {
display: flex;
justify-content: space-between;
margin: 5px 0;
padding: 2px 0;
border-bottom: 1px solid #e5e7eb;
}
.route-popup .stat:last-of-type {
border-bottom: none;
}
.route-popup .stat strong {
color: #374151;
}
.route-popup .view-btn {
display: inline-block;
margin-top: 10px;
padding: 8px 16px;
background: linear-gradient(45deg, #f97316, #ea580c);
color: white;
text-decoration: none;
border-radius: 6px;
font-size: 0.9em;
transition: all 0.2s;
}
.route-popup .view-btn:hover {
background: linear-gradient(45deg, #ea580c, #dc2626);
transform: translateY(-1px);
}
.map-controls {
position: absolute;
top: 10px;
right: 10px;
z-index: 1000;
display: flex;
flex-direction: column;
gap: 5px;
}
.map-control-btn {
background: white;
border: 2px solid #ddd;
border-radius: 6px;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 16px;
transition: all 0.2s;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.map-control-btn:hover {
background: #f3f4f6;
transform: scale(1.05);
}
.map-control-btn.active {
background: #f97316;
color: white;
border-color: #ea580c;
}
</style>
</head>
<body>
<div id="map">
<div id="map-loading" class="map-loading">
<div class="spinner"></div>
<div>Loading adventure routes...</div>
</div>
</div>
<!-- Map Controls -->
<div class="map-controls">
<button id="refresh-map" class="map-control-btn" title="Refresh map">🔄</button>
</div>
<script>
let map;
let routeLayer;
let routesVisible = true;
function initializeMap() {
console.log('Initializing standalone map...');
// Romania center and zoom for default view
const romaniaCenter = [45.9432, 24.9668]; // Center of Romania
const romaniaZoom = 7;
// Create map
map = L.map('map', {
zoomControl: true,
scrollWheelZoom: false, // Disable default scroll wheel zoom
doubleClickZoom: true,
touchZoom: true
}).setView(romaniaCenter, romaniaZoom);
// Enable zoom with Ctrl/Cmd + wheel only
map.getContainer().addEventListener('wheel', function(e) {
if ((e.ctrlKey || e.metaKey) && map.options.scrollWheelZoom !== true) {
map.options.scrollWheelZoom = true;
} else if (!(e.ctrlKey || e.metaKey) && map.options.scrollWheelZoom !== false) {
map.options.scrollWheelZoom = false;
}
if (!(e.ctrlKey || e.metaKey)) {
e.preventDefault(); // Prevent zoom if not holding Ctrl/Cmd
}
}, { passive: false });
// Add OpenStreetMap tiles
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
maxZoom: 18,
minZoom: 5
}).addTo(map);
console.log('Base map created');
// Create route layer group
routeLayer = L.layerGroup().addTo(map);
// Load routes
loadRoutes();
// Setup controls
setupControls();
}
function loadRoutes() {
console.log('Loading routes from API...');
// Get the parent window's origin for API calls
const apiUrl = window.location.origin + '/community/api/routes';
fetch(apiUrl)
.then(response => {
console.log('API response status:', response.status);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(routesData => {
console.log(`Loaded ${routesData.length} routes`);
// Hide loading indicator
const loading = document.getElementById('map-loading');
if (loading) {
loading.style.display = 'none';
}
if (routesData.length === 0) {
console.log('No routes found');
return;
}
// Route colors
const colors = [
'#f97316', '#dc2626', '#059669', '#7c3aed',
'#db2777', '#2563eb', '#7c2d12', '#065f46'
];
const allBounds = [];
// Add each route
routesData.forEach((route, index) => {
if (route.coordinates && route.coordinates.length > 0) {
const color = colors[index % colors.length];
// Create polyline
const polyline = L.polyline(route.coordinates, {
color: color,
weight: 4,
opacity: 0.8,
smoothFactor: 1
});
// Create popup content
const popupContent = `
<div class="route-popup">
<h3>🏍️ ${route.title}</h3>
<div class="stat">
<span>👤 Author:</span>
<strong>${route.author}</strong>
</div>
<div class="stat">
<span>📏 Distance:</span>
<strong>${route.distance.toFixed(2)} km</strong>
</div>
<div class="stat">
<span>⛰️ Elevation Gain:</span>
<strong>${route.elevation_gain.toFixed(0)} m</strong>
</div>
<div class="stat">
<span>🏔️ Max Elevation:</span>
<strong>${route.max_elevation.toFixed(0)} m</strong>
</div>
<a href="${route.url}" target="_parent" class="view-btn">
🔍 View Adventure Details
</a>
</div>
`;
polyline.bindPopup(popupContent);
routeLayer.addLayer(polyline);
// Add start and end markers
const startCoord = route.coordinates[0];
const endCoord = route.coordinates[route.coordinates.length - 1];
// Green start marker
const startMarker = L.marker(startCoord, {
icon: L.icon({
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
shadowSize: [41, 41]
})
}).bindPopup(`<b>Start of route</b><br>${route.title}`);
routeLayer.addLayer(startMarker);
// Red end marker
const endMarker = L.marker(endCoord, {
icon: L.icon({
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
shadowSize: [41, 41]
})
}).bindPopup(`<b>End of route</b><br>${route.title}`);
routeLayer.addLayer(endMarker);
// Collect bounds for fitting
allBounds.push(...route.coordinates);
console.log(`Added route: ${route.title} (${route.coordinates.length} points)`);
}
});
console.log(`Successfully loaded ${routesData.length} routes`);
// Do NOT auto-fit map to routes on load; keep Romania view/zoom
// Only fit to routes when user clicks the 🎯 button
// (see setupControls)
})
.catch(error => {
console.error('Error loading routes:', error);
const loading = document.getElementById('map-loading');
if (loading) {
loading.innerHTML = `
<div style="color: #dc2626;">
<div style="font-size: 1.2em; margin-bottom: 8px;">⚠️</div>
<div>Failed to load routes</div>
<div style="font-size: 0.8em; margin-top: 5px; color: #6b7280;">
${error.message}
</div>
</div>
`;
}
});
}
function setupControls() {
// Only refresh map button remains
// Refresh map button
document.getElementById('refresh-map').addEventListener('click', () => {
routeLayer.clearLayers();
loadRoutes();
});
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeMap);
} else {
initializeMap();
}
// Handle resize
window.addEventListener('resize', () => {
if (map) {
setTimeout(() => map.invalidateSize(), 100);
}
});
</script>
</body>
</html>