4E e 4I: l'uso del file di testo con il Php e la libreria Leaflet per la geolocalizzazione (prima parte)
Creare un'applicazione web con una semplice area riservata. Gli utenti autenticati possono utilizzare la geolocalizzazione del proprio browser per trovare la colonnina di ricarica elettrica più vicina (tra quelle memorizzate in un file di testo) e visualizzare la posizione sulla mappa.
📂 Struttura dei File
Per questo progetto, avrai bisogno della seguente struttura di file:
/tuo-progetto/
|
|-- login.php (La pagina con il form di login)
|-- login_process.php (Script PHP per verificare il login)
|-- map.php (L'area riservata con la mappa)
|-- logout.php (Script PHP per distruggere la sessione)
|-- get_stations.php (Script PHP che legge il file .txt e restituisce JSON)
|-- stations.txt (Il nostro file di testo con le coordinate)
|-- script.js (Il codice JavaScript per Leaflet e la logica)
```
Passo 1: Il File di Dati (File di Testo)
Creiamo il nostro "archivio". Sarà un semplice file CSV (Comma Separated Values) che chiameremo `stations.txt`.
📄 `stations.txt`
Crea questo file e salvalo nella cartella del progetto. Contiene nome, latitudine e longitudine.
Stazione Lodi Centro,45.3149,9.5025
Stazione Ospedale,45.3180,9.4980
Stazione Parco Tecnologico,45.3085,9.5100
Stazione Stazione FS,45.3120,9.5010
Passo 2: Il Backend (PHP)
Questo si occuperà dell'autenticazione e della fornitura dei dati.
🔐 `login.php` (Form di Login)
Questa è la pagina di accesso. È un semplice file HTML (salvato come `.php` per coerenza).
<?php
session_start();
// Se l'utente è già loggato, reindirizza alla mappa
if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
header('Location: map.php');
exit;
}
?>
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login Area Riservata</title>
<style>
body { font-family: sans-serif; display: grid; place-items: center; min-height: 80vh; }
form { border: 1px solid #ccc; padding: 20px; border-radius: 8px; }
div { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; }
input { width: 100%; padding: 8px; box-sizing: border-box; }
</style>
</head>
<body>
<form action="login_process.php" method="POST">
<h2>Login Mappa Ricarica</h2>
<?php if(isset($_GET['error'])): ?>
<p style="color: red;">Username o password errati!</p>
<?php endif; ?>
<div>
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit">Entra</button>
</form>
</body>
</html>
⚙️ `login_process.php` (Logica di Login)
Questo script (solo PHP) controlla le credenziali. Per questo esercizio, usiamo credenziali "hardcoded" (scritte direttamente nel codice). **Non fare mai così in un'applicazione reale\!**
<?php
// Inizia la sessione
session_start();
// Credenziali "mock" per l'esercizio
$utente_valido = 'user';
$password_valida = 'pass123';
// Controlla se i dati sono stati inviati in POST
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
// Verifica le credenziali
if ($username === $utente_valido && $password === $password_valida) {
// Credenziali corrette! Salva lo stato nella sessione
$_SESSION['loggedin'] = true;
$_SESSION['username'] = $username;
// Reindirizza alla pagina della mappa
header('Location: map.php');
exit;
} else {
// Credenziali errate, reindirizza al login con un errore
header('Location: login.php?error=1');
exit;
}
} else {
// Se qualcuno prova ad accedere direttamente a questo file, reindirizza al login
header('Location: login.php');
exit;
}
?>
📍 `get_stations.php` (API per i Dati)
Questo script legge il file `stations.txt`, lo converte in un array PHP e infine lo "stampa" in formato **JSON**, che è perfetto per essere letto da JavaScript.
<?php
// Imposta l'header per far capire al browser che stiamo inviando JSON
header('Content-Type: application/json');
$file_path = 'stations.txt';
$stations = [];
// Controlla se il file esiste
if (file_exists($file_path)) {
// Apre il file in modalità lettura
if (($handle = fopen($file_path, "r")) !== FALSE) {
// Legge il file riga per riga (fino a 1000 caratteri) usando la virgola come separatore
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
// Assicurati che ci siano 3 colonne (nome, lat, lng)
if (count($data) == 3) {
$stations[] = [
'name' => $data[0],
'lat' => (float)$data[1], // Converte in numero (float)
'lng' => (float)$data[2] // Converte in numero (float)
];
}
}
fclose($handle);
}
}
// Codifica l'array di stazioni in una stringa JSON e la invia
echo json_encode($stations);
?>
🔒 `logout.php` (Uscita)
Script semplice per "dimenticare" l'utente e terminare la sessione.
<?php
session_start();
// Rimuove tutte le variabili di sessione
session_unset();
// Distrugge la sessione
session_destroy();
// Reindirizza alla pagina di login
header('Location: login.php');
exit;
?>
Passo 3: Il Frontend (HTML, JavaScript, Leaflet)
Questa è l'area riservata che l'utente vede dopo il login.
🗺️ `map.php` (La Pagina Riservata)
Questa pagina prima controlla se l'utente è loggato. Se lo è, mostra la mappa.
<?php
session_start();
// Blocco di sicurezza: controlla se l'utente è loggato
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
header('Location: login.php'); // Se non è loggato, via al login
exit;
}
?>
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mappa Stazioni - Area Riservata</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
crossorigin=""></script>
<style>
body { font-family: sans-serif; margin: 0; padding: 0; }
header { padding: 10px 20px; background: #f4f4f4; border-bottom: 1px solid #ccc; display: flex; justify-content: space-between; align-items: center; }
/* La mappa DEVE avere un'altezza definita */
#map { height: 80vh; width: 100%; }
button { padding: 10px 15px; cursor: pointer; }
</style>
</head>
<body>
<header>
<h1>Ciao, <?php echo htmlspecialchars($_SESSION['username']); ?>!</h1>
<a href="logout.php">Logout</a>
</header>
<div style="padding: 20px;">
<button id="find-nearest">📍 Trova la colonnina più vicina a me</button>
</div>
<div id="map"></div>
<script src="script.js"></script>
</body>
</html>
🌍 `script.js` (Logica JavaScript e Leaflet)
Questo è il cuore dell'applicazione frontend.
// Attende che il DOM sia completamente caricato prima di eseguire il codice
document.addEventListener('DOMContentLoaded', () => {
// --- 1. Inizializzazione Mappa ---
// Coordinate di Lodi (per centrare la mappa all'inizio)
const lodiCoords = [45.314, 9.502];
// Crea l'oggetto mappa e lo lega al <div> con id="map"
const map = L.map('map').setView(lodiCoords, 13);
// Aggiunge il "layer" di OpenStreetMap (le mattonelle della mappa)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
// Variabili globali per memorizzare i dati
let allStations = []; // Conterrà le stazioni caricate
let userMarker = null; // Marker per la posizione dell'utente
let routeLine = null; // Linea per il "percorso"
// --- 2. Caricamento Stazioni ---
// Usiamo fetch per chiamare il nostro script PHP
fetch('get_stations.php')
.then(response => response.json()) // Converte la risposta in JSON
.then(data => {
allStations = data;
// Aggiunge un marker per ogni stazione
allStations.forEach(station => {
L.marker([station.lat, station.lng])
.addTo(map)
.bindPopup(`<b>${station.name}</b>`); // Aggiunge un popup al click
});
})
.catch(error => console.error('Errore nel caricamento delle stazioni:', error));
// --- 3. Logica del Bottone "Trova più vicina" ---
const findButton = document.getElementById('find-nearest');
findButton.addEventListener('click', () => {
// Controlla se il browser supporta la geolocalizzazione
if (!navigator.geolocation) {
alert('Geolocalizzazione non supportata dal tuo browser.');
return;
}
findButton.textContent = 'Ricerca posizione...';
findButton.disabled = true;
// Chiede la posizione all'utente
navigator.geolocation.getCurrentPosition(onPositionSuccess, onPositionError);
});
// --- 4. Funzioni Helper ---
/**
* Chiamata se la geolocalizzazione ha successo
*/
function onPositionSuccess(position) {
findButton.textContent = '📍 Trova la colonnina più vicina a me';
findButton.disabled = false;
const userLat = position.coords.latitude;
const userLng = position.coords.longitude;
const userLatLng = [userLat, userLng];
// Aggiunge o sposta il marker dell'utente
if (userMarker) {
userMarker.setLatLng(userLatLng);
} else {
// Crea un'icona personalizzata (blu) per l'utente
const blueIcon = L.icon({
iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41],
// Applica un filtro CSS per colorare l'icona di blu
className: 'user-marker'
});
// Aggiungi questo stile nell'HTML o in un file CSS per il filtro blu:
// <style> .user-marker { filter: hue-rotate(180deg); } </style>
userMarker = L.marker(userLatLng).addTo(map)
.bindPopup('<b>La tua posizione</b>');
}
// Centra la mappa sulla posizione dell'utente
map.setView(userLatLng, 15);
// Trova la stazione più vicina
const nearest = findNearestStation(userLat, userLng);
if (nearest) {
alert(`Stazione più vicina trovata:\n${nearest.station.name}\nDistanza: ${nearest.distance.toFixed(2)} km`);
// Rimuove la linea precedente se esiste
if (routeLine) {
map.removeLayer(routeLine);
}
// Disegna una linea dritta (NOTA: non è un percorso stradale!)
routeLine = L.polyline(
[userLatLng, [nearest.station.lat, nearest.station.lng]],
{ color: 'blue', weight: 5, opacity: 0.7 }
).addTo(map);
// Adatta lo zoom per mostrare entrambi i punti
map.fitBounds([userLatLng, [nearest.station.lat, nearest.station.lng]], { padding: [50, 50] });
} else {
alert('Nessuna stazione trovata nel database.');
}
}
/**
* Chiamata se la geolocalizzazione fallisce
*/
function onPositionError(error) {
findButton.textContent = '📍 Trova la colonnina più vicina a me';
findButton.disabled = false;
alert(`Errore nell'ottenere la posizione: ${error.message}`);
}
/**
* Trova la stazione più vicina date le coordinate dell'utente
*/
function findNearestStation(userLat, userLng) {
if (allStations.length === 0) return null;
let minDistance = Infinity;
let nearestStation = null;
allStations.forEach(station => {
// Calcola la distanza tra utente e stazione
const distance = getDistance(userLat, userLng, station.lat, station.lng);
if (distance < minDistance) {
minDistance = distance;
nearestStation = station;
}
});
return { station: nearestStation, distance: minDistance };
}
/**
* Calcola la distanza "as-the-crow-flies" (linea d'aria)
* usando la formula Haversine. Restituisce la distanza in KM.
*/
function getDistance(lat1, lon1, lat2, lon2) {
const R = 6371; // Raggio della Terra in km
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLon = (lon2 - lon1) * Math.PI / 180;
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c; // Distanza in km
}
});
🖥️ Come Eseguire l'Esercizio
1. Server Locale: Non puoi semplicemente aprire `login.php` nel browser. Hai bisogno di un server web che esegua PHP. La soluzione più semplice è usare un software come **XAMPP**, **MAMP** (per Mac) o WAMP.
2. Installa XAMPP: Scaricalo e installalo.
3. Copia i File: Trova la cartella `htdocs` di XAMPP (di solito `C:\xampp\htdocs\`).
4. Crea la Cartella: Crea una nuova cartella dentro `htdocs` chiamata `progetto-mappa`.
5. Incolla i File: Copia tutti i file che abbiamo creato (`login.php`, `map.php`, `stations.txt`, ecc.) dentro la cartella `progetto-mappa`.
6. Avvia XAMPP: Avvia il pannello di controllo di XAMPP e fai clic su **Start** per i moduli "Apache" e "PHP" (se è un modulo separato, di solito è incluso in Apache).
7. Apri nel Browser: Apri il tuo browser (Chrome, Firefox, ecc.) e naviga a:
`http://localhost/progetto-mappa/login.php`
8. Testa: Prova a effettuare il login con:
Username: `user`
Password: `pass123`
⚠️ Nota Importante: "Percorso più Breve" vs "Punto più Vicino"
Questo esercizio trova il punto più vicino in linea d'aria (usando la formula Haversine).
L'utente ha chiesto il "percorso più breve". Questo implicherebbe un **calcolo di routing stradale** (come fa Google Maps). Quell'operazione è *estremamente* complessa e richiede servizi API esterni (come Mapbox, OSRM, o l'API di Google Maps) per calcolare la distanza seguendo la rete stradale.
Per un esercizio con solo PHP, JS e Leaflet, trovare il "punto più vicino" (in linea d'aria) è l'obiettivo corretto e realizzabile.
Commenti
Posta un commento