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: '&copy; <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

Post popolari in questo blog

Esercizi in Excel e fogli di Google

Simulazioni di reti (con Cisco Packet Tracer)