Tutti gli articoli

PWA · Next.js · Mapbox · Turf.js · GPS · 30 aprile 2026 · 7 min di lettura

Costruire una PWA di navigazione GPS con Next.js + Mapbox (case study Wayline)

Esperienza con Wayline, la mia PWA di navigazione GPS che calcola percorsi evitando zone definite. Stack Next.js 16 + Mapbox GL + SQLite + Turf.js, e le decisioni tecniche che ho preso per farla funzionare offline.

Il problema: navigare evitando certe zone

Tutti i GPS ti danno il percorso più veloce. Ma a volte vuoi evitare certe zone - un cantiere che blocca la strada ogni mattina, un quartiere che preferisci aggirare, una strada dove sai che c'è un autovelox.

È questa esigenza che ha dato origine a Wayline: una PWA di navigazione GPS dove definisci le tue zone di evitamento, e l'applicazione ricalcola automaticamente percorsi che le aggirano.

Ecco le scelte tecniche che ho fatto, e perché.

Lo stack: perché non un'app nativa?

Per la nav GPS, il riflesso è React Native o Flutter. Ho scelto PWA Next.js + Mapbox per diversi motivi:

  • Niente validazione App Store: deploy istantaneo, aggiornamenti senza review
  • Geolocalizzazione nativa via API Web: navigator.geolocation funziona bene quanto un'app nativa su iOS e Android recenti
  • Mapbox GL JS: motore di rendering vettoriale ultra-rapido, stesse capacità degli SDK nativi
  • Service Worker: posso cachare la mappa e i percorsi per l'uso offline
  • Niente attrito di installazione: l'utente apre l'URL, lo aggiunge alla home, fatto

L'unico vero trade-off: niente accesso alle notifiche push iOS fuori dalla PWA installata. Per Wayline non è critico - il GPS si usa uscendo, non in background.

Calcolare un percorso con evitamento di zone

È il cuore del progetto. Mapbox propone un'API di routing (Directions API) che prende dei waypoint e restituisce il percorso ottimale. Ma non sa "evitare tale poligono".

Il trucco: si calcola un percorso di base, si guarda se attraversa una zona di evitamento, e in caso affermativo, si inserisce un waypoint che forza la deviazione.

Per verificare l'intersezione tra la linea del percorso e il poligono, uso Turf.js:

import * as turf from '@turf/turf';

function routeIntersectsZone(routeGeoJSON, zonePolygon) {
  return turf.booleanIntersects(routeGeoJSON, zonePolygon);
}

function findDetourWaypoint(zonePolygon) {
  const center = turf.centroid(zonePolygon);
  const bbox = turf.bbox(zonePolygon);
  // Posiziona il waypoint leggermente all'esterno del nord della zona
  return [bbox[0] - 0.01, bbox[3] + 0.01];
}

L'algoritmo semplificato:

  1. Chiamata a Mapbox Directions con origine + destinazione
  2. Per ogni zona definita: check se il percorso la attraversa
  3. Se sì, calcola un waypoint di deviazione
  4. Ri-chiamata a Directions con il waypoint inserito
  5. Ritorna il percorso finale

Restano 1-2 chiamate API per calcolo di percorso, ma l'esperienza è fluida (<300ms in media).

Archiviazione locale con SQLite

Per le zone di evitamento e lo storico dei percorsi, ho scelto SQLite lato server invece di Postgres o un servizio gestito. Perché:

  • Wayline è un progetto personale, non serve scalare a migliaia di utenti
  • SQLite gira nello stesso processo Node.js (better-sqlite3), zero connection overhead
  • Backup semplici: un solo file .db, posso fare rsync sul NAS
  • Lettura quasi istantanea per le zone dell'utente (tipicamente 5-20 zone)

Per le query spaziali (questa zona contiene questo punto?), Turf.js fa il lavoro lato server. Non serve PostGIS.

const Database = require('better-sqlite3');
const db = new Database('wayline.db');

db.exec(`
  CREATE TABLE IF NOT EXISTS zones (
    id INTEGER PRIMARY KEY,
    user_id TEXT NOT NULL,
    name TEXT,
    geojson TEXT NOT NULL,
    created_at INTEGER
  );
`);

const getUserZones = db.prepare('SELECT * FROM zones WHERE user_id = ?');

Modalità offline con Service Worker

È il vero differenziatore PWA vs app nativa classica. Con un service worker, posso cachare:

  • La shell dell'app (HTML/CSS/JS)
  • I tile Mapbox già visitati (Mapbox GL gestisce questo nativamente con una cache configurata)
  • Le zone dell'utente (memorizzate in localStorage)
  • L'ultimo percorso calcolato

Risultato: l'utente può continuare a vedere la sua mappa e il suo percorso anche in perdita di rete, cosa che capita regolarmente in zona rurale o sotterraneo.

Setup minimo del service worker (con next-pwa):

// next.config.js
const withPWA = require('next-pwa')({
  dest: 'public',
  runtimeCaching: [
    {
      urlPattern: /^https:\/\/api\.mapbox\.com\/styles\//,
      handler: 'CacheFirst',
      options: { cacheName: 'mapbox-tiles', expiration: { maxAgeSeconds: 7 * 24 * 60 * 60 } }
    }
  ]
});

Le trappole incontrate

1. navigator.geolocation può mentire. Su alcuni browser mobili, la precisione varia da 5m a 500m da un frame all'altro. Ho dovuto implementare un Kalman filter semplificato per smorzare il tracking.

2. I tile Mapbox sono cari oltre il free tier. Se Wayline avesse traffico massiccio, passerei a un fornitore di tile open-source (Stadia Maps, Protomaps). Per ora, il free tier basta abbondantemente.

3. iOS Safari e il service worker. Apple ha le sue regole: i service worker vengono scaricati aggressivamente, la cache non è garantita. Per usi critici (un ciclista che deve poter consultare il suo percorso), raccomando di raddoppiare con localStorage.

Il bilancio

MetricaValore
Bundle JS iniziale~140 KB gzip
Time to Interactive<2s su 4G
Ricalcolo percorso con evitamento250-350 ms
Funzionamento offline✅ Sì (mappa + ultimo percorso)
Archiviazione lato utente~3 MB dopo diversi percorsi

Cosa ne traggo

Per il 95% dei casi d'uso GPS, una PWA ben fatta sostituisce un'app nativa. L'unico vero interesse di un'app nativa per un progetto di navigazione è l'uso in background continuo (tipo Waze che ti parla in guida). Se non hai bisogno di questo, la PWA è più rapida da sviluppare, più facile da deployare, e offre un'esperienza paragonabile.

Mapbox + Turf.js è una combinazione potente: Mapbox per il rendering e il routing standard, Turf.js per tutto ciò che esce dalla loro API (intersezioni, buffer, distanze custom). Con questi due mattoni, puoi costruire praticamente qualsiasi logica geospaziale nel browser.

Un progetto simile?

Preventivo su misura in 24 h, senza impegno.

Richiedi un preventivo