Librería: Mapbox
Descripción: El usuario agrega varios puntos sobre el mapa y el sistema captura los puntos, genera la ruta y muestra la distancia y el tiempo estimado.
Archivo CSS
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
.control-panel {
position: absolute;
top: 10px;
right: 10px;
padding: 10px;
background: white;
border-radius: 4px;
max-width: 300px;
z-index: 1;
}
#points-list {
margin-top: 10px;
max-height: 200px;
overflow-y: auto;
}
.point-item {
padding: 5px;
border-bottom: 1px solid #eee;
}
button {
margin-top: 10px;
padding: 8px;
background: #3887be;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #2a65a0;
}
.marker-label {
position: absolute;
top: -25px; /* Ajusta esta distancia si es necesario */
left: 50%;
transform: translateX(-50%);
background-color: #e02a46;
color: white;
padding: 2px 5px;
border-radius: 5px;
font-size: 12px;
font-weight: bold;
white-space: nowrap;
}
Archivo HMTL:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Analisis de Rutas Optimizadas</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="<https://api.mapbox.com/mapbox-gl-js/v2.14.1/mapbox-gl.css>" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="mapa.css">
</head>
<body>
<div id="map"></div>
<div class="control-panel">
<h3>Analisis de Rutas Optimizadas</h3>
<p>Haz clic en el mapa para agregar puntos de parada.</p>
<div id="points-list"></div>
<button id="calculate-route">Calcular Ruta Óptima</button>
<button id="clear-points">Limpiar Puntos</button>
<div id="route-info"></div>
</div>
<script src="<https://api.mapbox.com/mapbox-gl-js/v2.14.1/mapbox-gl.js>"></script>
<script src="mapa.js"></script>
</body>
</html>
Archivo JavaScript:
// Agrega tu token
mapboxgl.accessToken = 'TU TOKEN XXXXXX';
// Inicializar el mapa en las coordenadas y zoom requeridas
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v12',
center: [-74.082, 4.639], // Ajustar a la ubicación deseada
zoom: 14
});
// Variables para almacenar los datos de los puntos
const markers = [];
const waypoints = [];
let routeSource = null;
// Función para agregar un marcador al hacer clic en el mapa
map.on('click', (e) => {
const coordinates = [e.lngLat.lng, e.lngLat.lat];
const marker = new mapboxgl.Marker({ // Crear un marcador y agregarlo al mapa
color: 'blue',
draggable: true
})
.setLngLat(coordinates)
.addTo(map);
const label = document.createElement('div'); // Crear un contenedor para el número de la parada
label.className = 'marker-label';
label.textContent = `Punto. ${markers.length + 1}`; // Número de la parada (parada 1, parada 2, etc.)
marker.getElement().appendChild(label); // Añadir el número de la parada al marcador
marker.on('dragend', () => { // Actualizar coordenadas al arrastrar el marcador
const newCoords = marker.getLngLat();
const index = markers.indexOf(marker);
if (index !== -1) {
waypoints[index] = [newCoords.lng, newCoords.lat];
updatePointsList();
}
});
markers.push(marker); // Almacenar el marcador y las coordenadas
waypoints.push(coordinates);
updatePointsList(); // Actualizar la lista de puntos
});
// Función para actualizar la lista de puntos en el panel
function updatePointsList() {
const pointsList = document.getElementById('points-list');
pointsList.innerHTML = '';
waypoints.forEach((point, index) => {
const pointItem = document.createElement('div');
pointItem.className = 'point-item';
pointItem.innerHTML = `Punto ${index + 1}: [${point[0].toFixed(4)}, ${point[1].toFixed(4)}]
<button class="remove-point" data-index="${index}">Eliminar</button>`;
pointsList.appendChild(pointItem);
});
document.querySelectorAll('.remove-point').forEach(button => { // Agregar event listeners a botones de eliminar
button.addEventListener('click', (e) => {
const index = parseInt(e.target.getAttribute('data-index'));
removePoint(index);
});
});
}
// Función para eliminar un punto
function removePoint(index) {
if (index >= 0 && index < markers.length) {
markers[index].remove(); // Eliminar marcador del mapa
markers.splice(index, 1); // Eliminar del array de marcadores
waypoints.splice(index, 1); // Eliminar del array de waypoints
updatePointsList(); // Actualizar lista
}
}
// Botón para calcular la ruta
document.getElementById('calculate-route').addEventListener('click', () => {
calculateOptimizedRoute();
});
// Función para limpiar puntos
document.getElementById('clear-points').addEventListener('click', () => {
clearAllPoints();
});
// Función para calcular la ruta optimizada
async function calculateOptimizedRoute() {
if (waypoints.length < 2) { // Verificar que tenemos suficientes puntos
alert('Se necesitan al menos 2 puntos para calcular una ruta.');
return;
}
try {
const coordinates = waypoints.map(point => point.join(',')); // Preparar los waypoints en formato correcto
const baseUrl = `https://api.mapbox.com/directions/v5/mapbox/driving-traffic/${coordinates.join(';')}`; // Crear URL base para la petición a la API de Direcciones de Mapbox
const params = new URLSearchParams({ // Iniciar con parámetros básicos
alternatives: 'true',
geometries: 'geojson',
annotations: 'distance',
overview: 'full',
steps: 'true',
access_token: mapboxgl.accessToken,
});
const url = `${baseUrl}?${params.toString()}`; // Construir URL completa
console.log("URL de solicitud: " + url);
const response = await fetch(url); // Realizar la petición
const data = await response.json();
if (data.code !== 'Ok') {
console.error("Error de la API:", data.message);
alert(`Error de la API: ${data.message}`);
return;
}
console.log(data); // Para ver la respuesta completa de la API
displayRoute(data); // Mostrar la ruta en el mapa
displayRouteInfo(data); // Mostrar información de la ruta
} catch (error) {
console.error('Error al calcular la ruta:', error);
alert('Hubo un error al calcular la ruta. Verifica la consola para más detalles.');
}
}
// Función para mostrar la ruta en el mapa
function displayRoute(routeData) {
if (map.getSource('route')) { // Si ya existe una capa de ruta, eliminarla
map.removeLayer('route-layer');
map.removeSource('route');
}
map.addSource('route', { // Agregar la nueva ruta como fuente y capa
type: 'geojson',
data: {
type: 'Feature',
properties: {},
geometry: routeData.routes[0].geometry
}
});
map.addLayer({
id: 'route-layer',
type: 'line',
source: 'route',
layout: {
'line-join': 'round',
'line-cap': 'round'
},
paint: {
'line-color': '#7525be',
'line-width': 5,
'line-opacity': 0.75
}
});
const coordinates = routeData.routes[0].geometry.coordinates; // Ajustar el mapa para mostrar toda la ruta
const bounds = coordinates.reduce((bounds, coord) => {
return bounds.extend(coord);
}, new mapboxgl.LngLatBounds(coordinates[0], coordinates[0]));
map.fitBounds(bounds, {
padding: 50
});
}
// Función para mostrar información de la ruta
function displayRouteInfo(routeData) {
const route = routeData.routes[0];
const distance = (route.distance / 1000).toFixed(2); // Convertir a km
const duration = Math.floor(route.duration / 60); // Convertir a minutos
let waypointOrder = ''; // Crear un mapeo del orden optimizado de paradas
if (routeData.waypoints && Array.isArray(routeData.waypoints)) {
waypointOrder = routeData.waypoints.map((wp, idx) => {
return idx + 1; // Usar el índice de los waypoints de la respuesta
}).join(' → ');
} else {
waypointOrder = 'No disponible';
}
const routeInfo = document.getElementById('route-info');
routeInfo.innerHTML = `
<h4>Información de la Ruta</h4>
<p>Distancia total: ${distance} km</p>
<p>Tiempo estimado: ${duration} minutos</p>
<p>Orden de paradas optimizado:</p>
<ul>
${waypointOrder.split(' → ').map(point => `<li>${point}</li>`).join('')}
</ul>
`;
console.log(routeData)
}
// Función para limpiar todos los puntos
function clearAllPoints() {
markers.forEach(marker => marker.remove()); // Eliminar todos los marcadores del mapa
markers.length = 0; // Limpiar arrays
waypoints.length = 0;
updatePointsList(); // Actualizar lista de puntos
if (map.getSource('route')) { // Eliminar ruta si existe
map.removeLayer('route-layer');
map.removeSource('route');
}
document.getElementById('route-info').innerHTML = ''; // Limpiar información de ruta
}