WildSOS nasce da una situazione concreta: qualcuno trova un animale selvatico in difficoltà, non sa cosa fare e non sa a chi rivolgersi. Chi è lì è agitato, spesso al telefono, spesso con poca batteria. L’obiettivo che mi sono dato è netto: una risposta utile entro 30 secondi dall’apertura del sito. Qui racconto come funziona la ricerca che lo rende possibile e dove sono i suoi limiti.
1. La ricerca gira nel browser
Il primo passo è capire che animale è. Il dataset delle specie (oltre 50) è un file locale con nome, sinonimi, tag e frasi descrittive, e la ricerca è fuzzy e lato client con Fuse.js:
const fuse = new Fuse(animali, {
keys: [
{ name: 'nome', weight: 2 },
{ name: 'sinonimi', weight: 1.5 },
{ name: 'cerca_per', weight: 1.2 },
{ name: 'tag', weight: 1 },
],
threshold: 0.45,
minMatchCharLength: 3,
includeScore: true,
})
Nessuna chiamata al server: i risultati compaiono mentre scrivi e tollerano i refusi. Con la configurazione reale «ricio» trova il riccio, «gheppo» il gheppio e «pipistrelo» il pipistrello. I pesi fanno sì che il nome conti più del sinonimo e il sinonimo più del tag.
2. Come parla chi non è un esperto
Nessuno scrive «Erinaceus europaeus». Per questo ogni specie ha dei sinonimi e un campo cerca_per con frasi che userebbe una persona qualunque. Per esempio, «animale con le spine» porta al riccio e «topo volante» al pipistrello.
Il limite: queste frasi le ho scritte per le specie più frequenti, non per tutte. È la cosa che so di dover estendere.
3. Distanza: la formula più semplice che basta
Scelto l’animale, mostro i centri più vicini. La distanza è in linea d’aria, con la formula di Haversine:
export function haversineKm(lat1: number, lng1: number, lat2: number, lng2: number): number {
const R = 6371
const toRad = (deg: number) => (deg * Math.PI) / 180
const dLat = toRad(lat2 - lat1)
const dLng = toRad(lng2 - lng1)
const a =
Math.sin(dLat / 2) ** 2 +
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) ** 2
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
}
Non uso un servizio di routing: servirebbe una chiave, costerebbe e non cambierebbe la decisione («chiama il centro più vicino»). Il limite lo dichiaro: la linea d’aria non è il tempo di guida.
4. Il centro più vicino non è sempre quello giusto
Un centro a pochi chilometri che non tratta rapaci non serve a chi ha trovato un falco. Ogni animale ha una categoria (rapace, uccello, mammifero, rettile, anfibio) e ogni centro dichiara le specie che gestisce. Confronto la categoria con un elenco di parole chiave e metto prima i centri che gestiscono quella categoria, in ordine di distanza, e poi tutti gli altri.
Due scelte contano: non escludo mai i centri senza corrispondenza (se nessuno corrisponde mostro l’elenco completo) e la corrispondenza è un confronto di testo, non un’ontologia: è grezzo, ma robusto rispetto a come i centri scrivono le specie.
5. Quando la posizione non c’è
Il browser può rifiutare la geolocalizzazione, o non averla. La richiesta ha un timeout di 10 secondi e, se non va a buon fine, compare un campo per scrivere città o CAP, che trasformo in coordinate con OpenStreetMap (Nominatim). È una scelta coerente con il resto del progetto: mappe con Leaflet e OpenStreetMap, nessun servizio a pagamento. Va detta anche una cosa di privacy: il testo che scrivi nel campo città viene inviato a quel servizio.
6. Un link che precompila la ricerca
Le card degli animali in home portano a /emergenza?q=riccio, e la ricerca parte già compilata. Il bot Telegram usa la stessa logica (ricerca fuzzy sugli animali e centri vicini), senza dipendenze in più.
Limiti che dichiaro
- La ricerca fuzzy è permissiva. Con parole corte può proporre specie che assomigliano per lettere: per questo mostro fino a sei risultati e lascio scegliere alla persona.
- La distanza è in linea d’aria, non un tempo di guida.
- I dati dei centri vanno verificati. Per questo la scheda dice quando un centro ha confermato i propri dati.
Cosa ho imparato
- In emergenza si scansiona, non si legge: un’unica azione principale e risposte immediate battono qualsiasi funzione in più.
- Fare il lavoro nel browser elimina latenza e punti di rottura.
- Dichiarare i limiti fa parte del prodotto.
