Auto commit: 2025-11-24T22:48:50.325Z
This commit is contained in:
parent
909621e9be
commit
fc7141b6a0
38
api.php
38
api.php
@ -2,14 +2,40 @@
|
|||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
require_once 'db/config.php';
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
$action = $_GET['action'] ?? 'get_current_locations';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$pdo = db();
|
$pdo = db();
|
||||||
$stmt = $pdo->query("SELECT t.matricula, t.modelo, lt.latitud, lt.longitud, lt.ultima_actualizacion
|
|
||||||
FROM localizacion_taxis lt
|
switch ($action) {
|
||||||
JOIN taxis t ON lt.id_taxi = t.id
|
case 'get_route_history':
|
||||||
ORDER BY lt.ultima_actualizacion DESC");
|
$id_taxi = $_GET['id_taxi'] ?? null;
|
||||||
$localizaciones = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
if (!$id_taxi) {
|
||||||
echo json_encode($localizaciones);
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Se requiere id_taxi']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare(
|
||||||
|
"SELECT latitud, longitud FROM localizacion_historico WHERE id_taxi = ? ORDER BY fecha_registro ASC"
|
||||||
|
);
|
||||||
|
$stmt->execute([$id_taxi]);
|
||||||
|
$history = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
echo json_encode($history);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'get_current_locations':
|
||||||
|
default:
|
||||||
|
$stmt = $pdo->query(
|
||||||
|
"SELECT t.matricula, t.modelo, lt.latitud, lt.longitud, lt.ultima_actualizacion " .
|
||||||
|
"FROM localizacion_taxis lt " .
|
||||||
|
"JOIN taxis t ON lt.id_taxi = t.id " .
|
||||||
|
"ORDER BY lt.ultima_actualizacion DESC"
|
||||||
|
);
|
||||||
|
$locations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
echo json_encode($locations);
|
||||||
|
break;
|
||||||
|
}
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
echo json_encode(['error' => 'Error de base de datos: ' . $e->getMessage()]);
|
echo json_encode(['error' => 'Error de base de datos: ' . $e->getMessage()]);
|
||||||
|
|||||||
169
localizacion.php
169
localizacion.php
@ -16,13 +16,23 @@ try {
|
|||||||
// Create localizacion_taxis table if it doesn't exist
|
// Create localizacion_taxis table if it doesn't exist
|
||||||
$pdo->exec("CREATE TABLE IF NOT EXISTS localizacion_taxis (
|
$pdo->exec("CREATE TABLE IF NOT EXISTS localizacion_taxis (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
id_taxi INT NOT NULL,
|
id_taxi INT NOT NULL UNIQUE, -- Un taxi solo puede tener una última ubicación
|
||||||
latitud DECIMAL(10, 8) NOT NULL,
|
latitud DECIMAL(10, 8) NOT NULL,
|
||||||
longitud DECIMAL(11, 8) NOT NULL,
|
longitud DECIMAL(11, 8) NOT NULL,
|
||||||
ultima_actualizacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
ultima_actualizacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (id_taxi) REFERENCES taxis(id)
|
FOREIGN KEY (id_taxi) REFERENCES taxis(id)
|
||||||
)");
|
)");
|
||||||
|
|
||||||
|
// Create location history table
|
||||||
|
$pdo->exec("CREATE TABLE IF NOT EXISTS localizacion_historico (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
id_taxi INT NOT NULL,
|
||||||
|
latitud DECIMAL(10, 8) NOT NULL,
|
||||||
|
longitud DECIMAL(11, 8) NOT NULL,
|
||||||
|
fecha_registro TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (id_taxi) REFERENCES taxis(id)
|
||||||
|
)");
|
||||||
|
|
||||||
// Handle form submission to add a new taxi for simplicity
|
// Handle form submission to add a new taxi for simplicity
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_taxi'])) {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_taxi'])) {
|
||||||
$matricula = trim($_POST['matricula']);
|
$matricula = trim($_POST['matricula']);
|
||||||
@ -40,21 +50,25 @@ try {
|
|||||||
$longitud = $_POST['longitud'];
|
$longitud = $_POST['longitud'];
|
||||||
|
|
||||||
if (!empty($id_taxi) && is_numeric($latitud) && is_numeric($longitud)) {
|
if (!empty($id_taxi) && is_numeric($latitud) && is_numeric($longitud)) {
|
||||||
// Check if a location for this taxi already exists
|
$pdo->beginTransaction();
|
||||||
$stmt_check = $pdo->prepare("SELECT id FROM localizacion_taxis WHERE id_taxi = ?");
|
try {
|
||||||
$stmt_check->execute([$id_taxi]);
|
// Upsert (Update or Insert) the last known location
|
||||||
$existing_location = $stmt_check->fetch();
|
$stmt_upsert = $pdo->prepare(
|
||||||
|
"INSERT INTO localizacion_taxis (id_taxi, latitud, longitud) VALUES (?, ?, ?) " .
|
||||||
|
"ON DUPLICATE KEY UPDATE latitud = VALUES(latitud), longitud = VALUES(longitud)"
|
||||||
|
);
|
||||||
|
$stmt_upsert->execute([$id_taxi, $latitud, $longitud]);
|
||||||
|
|
||||||
if ($existing_location) {
|
// Insert into history
|
||||||
// Update existing location
|
$stmt_history = $pdo->prepare("INSERT INTO localizacion_historico (id_taxi, latitud, longitud) VALUES (?, ?, ?)");
|
||||||
$stmt = $pdo->prepare("UPDATE localizacion_taxis SET latitud = ?, longitud = ? WHERE id_taxi = ?");
|
$stmt_history->execute([$id_taxi, $latitud, $longitud]);
|
||||||
$stmt->execute([$latitud, $longitud, $id_taxi]);
|
|
||||||
} else {
|
$pdo->commit();
|
||||||
// Insert new location
|
|
||||||
$stmt = $pdo->prepare("INSERT INTO localizacion_taxis (id_taxi, latitud, longitud) VALUES (?, ?, ?)");
|
|
||||||
$stmt->execute([$id_taxi, $latitud, $longitud]);
|
|
||||||
}
|
|
||||||
echo '<div class="alert alert-success" role="alert">Localización actualizada con éxito.</div>';
|
echo '<div class="alert alert-success" role="alert">Localización actualizada con éxito.</div>';
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
echo '<div class="alert alert-danger" role="alert">Error al guardar los datos: '. $e->getMessage() .'</div>';
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
echo '<div class="alert alert-danger" role="alert">Todos los campos son obligatorios y la latitud/longitud deben ser números.</div>';
|
echo '<div class="alert alert-danger" role="alert">Todos los campos son obligatorios y la latitud/longitud deben ser números.</div>';
|
||||||
}
|
}
|
||||||
@ -82,12 +96,42 @@ try {
|
|||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<i class="fas fa-map-marked-alt me-1"></i>
|
<i class="fas fa-map-marked-alt me-1"></i>
|
||||||
Mapa de Taxis
|
Mapa de Taxis
|
||||||
|
<button id="get-location-btn" class="btn btn-sm btn-outline-secondary float-end">Mi Ubicación</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="map" style="height: 500px;"></div>
|
<div id="map" style="height: 500px;"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header">
|
||||||
|
<i class="fas fa-route me-1"></i>
|
||||||
|
Historial de Ruta por Taxi
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form id="history-form">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="form-floating mb-3">
|
||||||
|
<select class="form-select" id="selectTaxiHistory" name="id_taxi" required>
|
||||||
|
<option value="">Seleccione un taxi para ver su historial</option>
|
||||||
|
<?php foreach ($taxis as $taxi): ?>
|
||||||
|
<option value="<?php echo $taxi['id']; ?>"><?php echo htmlspecialchars($taxi['matricula'] . ' - ' . $taxi['modelo']); ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<label for="selectTaxiHistory">Taxi</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 d-flex align-items-center">
|
||||||
|
<div class="d-grid w-100">
|
||||||
|
<button type="submit" class="btn btn-info">Mostrar Historial</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xl-6">
|
<div class="col-xl-6">
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
@ -160,24 +204,34 @@ try {
|
|||||||
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
// Coordenadas iniciales para centrar el mapa (ej. Madrid)
|
const initialCoords = [40.416775, -3.703790]; // Coordenadas iniciales (Madrid)
|
||||||
const initialCoords = [40.416775, -3.703790];
|
const map = L.map('map').setView(initialCoords, 6); // Zoom inicial más alejado
|
||||||
const map = L.map('map').setView(initialCoords, 12);
|
|
||||||
|
|
||||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
maxZoom: 19,
|
maxZoom: 19,
|
||||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
|
||||||
|
// Definir un icono personalizado para los taxis
|
||||||
|
const taxiIcon = L.icon({
|
||||||
|
iconUrl: 'https://cdn-icons-png.flaticon.com/512/15/15437.png', // URL de un icono de coche genérico
|
||||||
|
iconSize: [32, 32], // Tamaño del icono
|
||||||
|
iconAnchor: [16, 32], // Punto del icono que corresponde a la ubicación del marcador
|
||||||
|
popupAnchor: [0, -32] // Punto desde donde se abrirá el popup
|
||||||
|
});
|
||||||
|
|
||||||
let markers = {};
|
let markers = {};
|
||||||
|
|
||||||
function updateMap() {
|
function updateMap() {
|
||||||
fetch('api.php')
|
fetch('api.php')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
// Limpiar marcadores que ya no existen
|
const bounds = L.latLngBounds();
|
||||||
|
const activeMatriculas = data.map(taxi => taxi.matricula);
|
||||||
|
|
||||||
|
// Limpiar marcadores de taxis que ya no están en los datos
|
||||||
Object.keys(markers).forEach(matricula => {
|
Object.keys(markers).forEach(matricula => {
|
||||||
if (!data.find(taxi => taxi.matricula === matricula)) {
|
if (!activeMatriculas.includes(matricula)) {
|
||||||
map.removeLayer(markers[matricula]);
|
map.removeLayer(markers[matricula]);
|
||||||
delete markers[matricula];
|
delete markers[matricula];
|
||||||
}
|
}
|
||||||
@ -190,15 +244,23 @@ try {
|
|||||||
|
|
||||||
if (!isNaN(lat) && !isNaN(lon)) {
|
if (!isNaN(lat) && !isNaN(lon)) {
|
||||||
const popupContent = `<b>Taxi:</b> ${matricula}<br><b>Modelo:</b> ${taxi.modelo || 'N/A'}<br><b>Última vez:</b> ${taxi.ultima_actualizacion}`;
|
const popupContent = `<b>Taxi:</b> ${matricula}<br><b>Modelo:</b> ${taxi.modelo || 'N/A'}<br><b>Última vez:</b> ${taxi.ultima_actualizacion}`;
|
||||||
|
const latLng = [lat, lon];
|
||||||
|
|
||||||
if (markers[matricula]) {
|
if (markers[matricula]) {
|
||||||
markers[matricula].setLatLng([lat, lon]).setPopupContent(popupContent);
|
markers[matricula].setLatLng(latLng).setPopupContent(popupContent);
|
||||||
} else {
|
} else {
|
||||||
markers[matricula] = L.marker([lat, lon]).addTo(map)
|
markers[matricula] = L.marker(latLng, { icon: taxiIcon }).addTo(map)
|
||||||
.bindPopup(popupContent);
|
.bindPopup(popupContent);
|
||||||
}
|
}
|
||||||
|
// Extender los límites para el auto-zoom
|
||||||
|
bounds.extend(latLng);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Ajustar el mapa a los límites de los marcadores si hay alguno
|
||||||
|
if (bounds.isValid()) {
|
||||||
|
map.fitBounds(bounds, { padding: [50, 50] }); // Añade un poco de padding
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => console.error('Error al cargar las localizaciones:', error));
|
.catch(error => console.error('Error al cargar las localizaciones:', error));
|
||||||
}
|
}
|
||||||
@ -208,6 +270,71 @@ try {
|
|||||||
|
|
||||||
// Carga inicial
|
// Carga inicial
|
||||||
updateMap();
|
updateMap();
|
||||||
|
|
||||||
|
// Lógica para el historial de rutas
|
||||||
|
let routePolyline = null;
|
||||||
|
document.getElementById('history-form').addEventListener('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const taxiId = document.getElementById('selectTaxiHistory').value;
|
||||||
|
if (!taxiId) {
|
||||||
|
alert('Por favor, seleccione un taxi.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(`api.php?action=get_route_history&id_taxi=${taxiId}`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(historyData => {
|
||||||
|
if (routePolyline) {
|
||||||
|
map.removeLayer(routePolyline);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (historyData.length < 2) {
|
||||||
|
alert('No hay suficiente historial para mostrar una ruta.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const latLngs = historyData.map(point => [parseFloat(point.latitud), parseFloat(point.longitud)]);
|
||||||
|
|
||||||
|
routePolyline = L.polyline(latLngs, {color: '#007bff', weight: 5}).addTo(map);
|
||||||
|
|
||||||
|
map.fitBounds(routePolyline.getBounds(), { padding: [50, 50] });
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error al cargar el historial:', error));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Lógica para el botón "Mi Ubicación"
|
||||||
|
let userLocationMarker = null;
|
||||||
|
const userIcon = L.icon({ // Icono personalizado para el usuario
|
||||||
|
iconUrl: 'https://cdn-icons-png.flaticon.com/512/684/684809.png', // Icono de persona
|
||||||
|
iconSize: [32, 32],
|
||||||
|
iconAnchor: [16, 32],
|
||||||
|
popupAnchor: [0, -32]
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('get-location-btn').addEventListener('click', function() {
|
||||||
|
if (!navigator.geolocation) {
|
||||||
|
alert('La geolocalización no es soportada por tu navegador.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.geolocation.getCurrentPosition(function(position) {
|
||||||
|
const lat = position.coords.latitude;
|
||||||
|
const lon = position.coords.longitude;
|
||||||
|
const userLatLng = [lat, lon];
|
||||||
|
|
||||||
|
if (userLocationMarker) {
|
||||||
|
userLocationMarker.setLatLng(userLatLng);
|
||||||
|
} else {
|
||||||
|
userLocationMarker = L.marker(userLatLng, { icon: userIcon }).addTo(map)
|
||||||
|
.bindPopup('<b>Tu estás aquí</b>');
|
||||||
|
}
|
||||||
|
map.setView(userLatLng, 15); // Centrar el mapa en el usuario con un zoom más cercano
|
||||||
|
userLocationMarker.openPopup();
|
||||||
|
|
||||||
|
}, function() {
|
||||||
|
alert('No se pudo obtener tu ubicación. Asegúrate de haber concedido los permisos.');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user