Mappa interattiva dei sentieri, rifugi e vette del Club Alpino Italiano — Sezione di Bergamo.
Progressive Web App installabile, accessibile da desktop e dispositivi mobili.
| Tecnologia | Versione | Utilizzo |
|---|---|---|
| Svelte | 5 | Framework UI (runes mode) |
| Vite | 6 | Build tool e dev server |
| ArcGIS Maps SDK for JS | 5 | Mappa, layer, geocoding |
| Supabase | 2.x | Database PostGIS, auth, RLS, Edge Functions |
| OpenRouteService | v2 | Routing foot-hiking su OSM |
| Lucide Svelte | 1.x | Icone SVG |
┌─────────────────────────────────────────────────────────────┐
│ Frontend (PWA) │
│ Svelte 5 + Vite + ArcGIS Maps SDK │
├─────────────────────────────────────────────────────────────┤
│ Components │
│ ├── MapContainer → Mappa Esri, GeoJSON layers │
│ ├── CustomPopup → Lettura/modifica/eliminazione │
│ ├── AddFeature → Creazione feature + routing OSM │
│ ├── Navigate → Ricerca sentieri verso POI │
│ ├── AdminPanel → Gestione richieste (admin) │
│ ├── MyRequests → Stato richieste (utente) │
│ ├── Legend / Basemap → UI legenda e mappa base │
│ └── LocateButton → GPS + bussola │
├─────────────────────────────────────────────────────────────┤
│ Services │
│ ├── trailsService → CRUD + routing (via Edge Fn) │
│ └── requestsService → Submit/fetch/approve/delete │
├─────────────────────────────────────────────────────────────┤
│ Stores ($state) │
│ ├── mapStore → Vista, layer, popup, UI, quota │
│ ├── authStore → Sessione utente, ruoli │
│ └── themeStore → Tema chiaro/scuro/sistema │
└─────────────────────────────────────────────────────────────┘
│
┌───────────────┴────────────────┐
▼ ▼
┌─────────────────────────┐ ┌────────────────────────────────┐
│ Supabase (Backend) │ │ Edge Function: ors-route │
├─────────────────────────┤ ├────────────────────────────────┤
│ Database (PostGIS) │ │ Proxy sicuro per ORS │
│ ├── rifugi │ │ API key mai esposta al client │
│ ├── sentieri │ │ Rate limiting atomico via DB │
│ ├── vette │ │ Auth utente via Bearer token │
│ ├── requests │ └───────────┬────────────────────┘
│ └── settings │ │
├─────────────────────────┤ ▼
│ Auth │ ┌────────────────────────────────┐
│ └── Email/password │ │ OpenRouteService (API) │
├─────────────────────────┤ ├────────────────────────────────┤
│ RLS Policies │ │ Profilo: foot-hiking │
│ ├── Admin: full access │ │ Routing su grafo OSM │
│ ├── Utenti: own rows │ │ Rate limit: 2000/giorno │
│ └── Insert: own email │ │ Fallback: linea diretta │
├─────────────────────────┤ └────────────────────────────────┘
│ Stored Procedures │
│ ├── approve_request │ ← atomica con FOR UPDATE
│ └── check_ors_rate_limit│ ← atomica con FOR UPDATE
├─────────────────────────┤
│ Triggers │
│ └── updated_at (auto) │ ← su rifugi, sentieri, vette
├─────────────────────────┤
│ PostGIS Functions │
│ ├── get_trails_to_dest │
│ └── get_nearby_pois │
└─────────────────────────┘
Il tracciamento di nuovi sentieri usa l'API OpenRouteService (profilo foot-hiking) proxata tramite una Supabase
Edge Function (ors-route):
trailsService.js chiama la Edge Function autenticandosi con il Bearer token di sessionecheck_ors_rate_limit con FOR UPDATE) e
chiama ORS con la API key mai esposta al clientremaining/limit accanto alla propria emailrequests)supabase.auth.getUser() — non può essere falsificata dal clientapprove_request usa FOR UPDATE per prevenire doppia esecuzione
e garantisce rollback in caso di erroresrc/
├── App.svelte # Layout principale (panel, header con settings)
├── App.css # Stili panel, header, auth, mobile
├── global.css # CSS custom properties (tema chiaro/scuro), reset
├── main.js # Entry point
│
├── assets/
│ └── i18n/ # Localizzazione
│ ├── i18n.svelte.js # Store i18n ($state)
│ ├── it.json # Stringhe italiano
│ └── en.json # Stringhe inglese
│
├── components/
│ ├── map/
│ │ ├── MapContainer.svelte # Mappa Esri, GeoJSONLayer da Supabase, click handler
│ │ └── MapContainer.css
│ │
│ ├── panel/
│ │ ├── add/
│ │ │ ├── AddFeature.svelte # Aggiunta rifugi, vette e sentieri
│ │ │ └── AddFeature.css
│ │ ├── admin/
│ │ │ ├── AdminPanel.svelte # Pannello admin richieste pendenti
│ │ │ └── AdminPanel.css
│ │ ├── requests/
│ │ │ ├── MyRequests.svelte # Lista richieste utente con auto-refresh
│ │ │ └── MyRequests.css
│ │ ├── navigate/
│ │ │ ├── Navigate.svelte # Barra floating "Raggiungi"
│ │ │ └── Navigate.css
│ │ ├── locate/
│ │ │ ├── LocateButton.svelte # Geolocalizzazione, tracciamento GPS e bussola
│ │ │ └── LocateButton.css
│ │ ├── basemap/
│ │ │ ├── BasemapSwitcher.svelte # Selezione mappa base
│ │ │ └── BasemapSwitcher.css
│ │ └── legend/
│ │ ├── Legend.svelte # Legenda sentieri, rifugi e vette
│ │ └── Legend.css
│ │
│ └── popup/
│ ├── CustomPopup.svelte # Card popup con modifica, eliminazione e nearby POIs
│ └── CustomPopup.css
│
├── lib/
│ └── supabaseClient.js # Client Supabase
│
├── models/
│ └── schema.js # Schema tabelle (rifugi, sentieri, vette)
│
├── services/
│ ├── trailsService.js # CRUD Supabase + routing via Edge Function
│ └── requestsService.js # Richieste: submit, fetch, approve (RPC), delete
│
├── stores/
│ ├── authStore.svelte.js # Auth, ruoli, subscription cleanup
│ ├── mapStore.svelte.js # Stato mappa, popup, highlight, UI, quota ORS
│ └── themeStore.svelte.js # Stato tema (light/dark/system)
│
└── utils/
└── popupUtils.js # Utility per popup
public/
├── manifest.json # PWA manifest
├── service-worker.js # Service worker per caching
├── offline.html # Pagina offline
├── favicon.png
└── images/icons/ # Icone PWA (16–512px + maskable)
git clone <repo-url>
cd bergamo-trails-pwa
npm install
Copia .env.example in .env e configura:
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key
Nota: La chiave ORS non va nel
.envdel frontend. È gestita come secret server-side nella Edge Function
npm run dev
L'app sarà disponibile su http://localhost:5173.
npm run build
npm run preview # anteprima locale del build
I file di output vengono generati nella cartella dist/.
Il progetto include la configurazione per Netlify (netlify.toml).
Basta collegare il repository a Netlify e il deploy avverrà automaticamente.
[build]
command = "npm run build"
publish = "dist"
I dati geografici di partenza provengono dal servizio cartografico Maggioli S.p.A. per conto di CAI Bergamo e sono serviti tramite Supabase (PostGIS) in formato GeoJSON.
Il dataset originale dei sentieri e rifugi viene progressivamente arricchito dalla community con l'aggiunta di vette, nuovi punti di interesse e nuovi sentieri tracciati direttamente dall'app.
La funzionalità "Raggiungi" utilizza una funzione PostGIS (get_trails_to_destination) per calcolare i sentieri che
passano vicino a un rifugio o vetta, distinguendo tra accesso diretto (< 100m) e tramite collegamento.
L'indirizzo email fornito in fase di registrazione è utilizzato esclusivamente per identificare le richieste inviate all'admin (creazione, modifica, eliminazione di feature).
Non viene condiviso con terze parti né utilizzato per altri scopi.
Questo progetto è a uso privato. I dati cartografici di partenza sono di proprietà di CAI Bergamo / Maggioli S.p.A. e vengono integrati con contributi originali degli utenti.