# language: fr Fonctionnalité: API - Déclenchement GPS et géolocalisation audio-guides En tant que système backend Je veux calculer les déclenchements GPS et distances pour audio-guides Afin de permettre une expérience automatique en mode voiture/vélo/transport Contexte: Étant donné que l'API RoadWave est démarrée Et que l'utilisateur "user@example.com" est authentifié # 16.3.1 - Calcul de proximité et déclenchement Scénario: POST /api/v1/audio-guides/{id}/check-proximity - Vérification proximité Étant donné un audio-guide voiture avec 8 séquences Et que l'utilisateur est à la position (43.1233, 2.5677) Et que le prochain point GPS (séquence 2) est à (43.1245, 2.5690) avec rayon 30m Quand je fais un POST sur "/api/v1/audio-guides/ag_123/check-proximity": """json { "user_position": { "latitude": 43.1233, "longitude": 2.5677 }, "current_sequence": 1 } """ Alors le code HTTP de réponse est 200 Et le corps de réponse contient: """json { "in_trigger_zone": false, "next_sequence_id": "seq_2", "distance_to_next": 145.3, "eta_seconds": 18, "direction_degrees": 45, "should_trigger": false } """ Scénario: Déclenchement automatique dans rayon 30m (voiture) Étant donné un audio-guide voiture Et que l'utilisateur entre à 25m du point GPS suivant Quand je fais un POST sur "/api/v1/audio-guides/ag_123/check-proximity" Alors le code HTTP de réponse est 200 Et should_trigger est true Et in_trigger_zone est true Et le message "Séquence déclenchée automatiquement" est retourné Plan du Scénario: Rayon de déclenchement selon mode Étant donné un audio-guide en mode Et un point GPS avec rayon par défaut Quand l'utilisateur entre à du point Alors should_trigger est Exemples: | mode | distance | trigger | | voiture | 25m | true | | voiture | 35m | false | | velo | 45m | true | | velo | 55m | false | | transport | 95m | true | | transport | 105m | false | # Calcul distance avec PostGIS Scénario: Calcul distance avec ST_Distance (geography) Étant donné deux points GPS: | point | latitude | longitude | | Position user| 43.1234 | 2.5678 | | Point séq. 2 | 43.1245 | 2.5690 | Quand le calcul PostGIS ST_Distance est effectué Alors la distance retournée est 145.3 mètres Et le calcul utilise le type geography (WGS84) Et la précision est au mètre près Scénario: Calcul ETA basé sur vitesse actuelle Étant donné que l'utilisateur est à 320m du prochain point Et que sa vitesse actuelle est 28 km/h Quand l'ETA est calculé Alors l'ETA retourné est 41 secondes Et la formule appliquée est: (distance_m / 1000) / (vitesse_kmh) * 3600 Scénario: ETA non calculé si vitesse < 5 km/h Étant donné que l'utilisateur est à 200m du prochain point Et que sa vitesse actuelle est 2 km/h (arrêté) Quand l'ETA est calculé Alors l'ETA retourné est null Et le message "En attente de déplacement" est inclus Scénario: Calcul direction (bearing) avec PostGIS Étant donné la position utilisateur (43.1234, 2.5678) Et le prochain point (43.1245, 2.5690) au nord-est Quand le bearing est calculé avec ST_Azimuth Alors l'angle retourné est 45° (nord-est) Et la flèche correspondante est "↗" Plan du Scénario: Conversion angle en flèche (8 directions) Étant donné un angle de ° Quand la flèche est calculée Alors la direction retournée est "" Exemples: | degrees | arrow | | 0 | ↑ | | 45 | ↗ | | 90 | → | | 135 | ↘ | | 180 | ↓ | | 225 | ↙ | | 270 | ← | | 315 | ↖ | # 16.3.3 - Gestion point manqué Scénario: Détection point manqué (hors rayon mais dans tolérance) Étant donné un audio-guide voiture Et un point GPS avec rayon 30m et tolérance 100m Et que l'utilisateur passe à 65m du point Quand je fais un POST sur "/api/v1/audio-guides/ag_123/check-proximity" Alors le code HTTP de réponse est 200 Et le corps de réponse contient: """json { "in_trigger_zone": false, "missed_point": true, "distance_to_point": 65, "tolerance_zone": true, "actions_available": ["listen_anyway", "skip", "navigate_back"] } """ Scénario: Point manqué au-delà tolérance (>100m en voiture) Étant donné un audio-guide voiture Et que l'utilisateur passe à 150m du point GPS Quand je fais un POST sur "/api/v1/audio-guides/ag_123/check-proximity" Alors missed_point est false Et tolerance_zone est false Et aucune popup "point manqué" n'est déclenchée Plan du Scénario: Rayon tolérance selon mode Étant donné un audio-guide en mode Et que l'utilisateur passe à du point Alors tolerance_zone est Exemples: | mode | distance | in_tolerance | | voiture | 60m | true | | voiture | 110m | false | | velo | 70m | true | | velo | 80m | false | | transport | 120m | true | | transport | 160m | false | # Progress bar dynamique Scénario: Calcul progress bar vers prochain point Étant donné que la distance initiale vers le prochain point était 500m Et que l'utilisateur est maintenant à 175m du point Quand le pourcentage de progression est calculé Alors le progress_percentage retourné est 65% Et la formule est: 100 - (distance_actuelle / distance_initiale * 100) # Gestion trajectoire et itinéraire Scénario: Calcul distance totale parcours Étant donné un audio-guide avec les points GPS suivants: | sequence | latitude | longitude | | 1 | 43.1234 | 2.5678 | | 2 | 43.1245 | 2.5690 | | 3 | 43.1250 | 2.5700 | Quand je fais un GET sur "/api/v1/audio-guides/ag_123/route-stats" Alors le code HTTP de réponse est 200 Et le corps de réponse contient: """json { "total_distance": 350, "distances_between_points": [ {"from": 1, "to": 2, "distance": 150}, {"from": 2, "to": 3, "distance": 200} ] } """ Scénario: Vérification cohérence itinéraire (alerte distance excessive) Étant donné un audio-guide en mode "pieton" Et deux points GPS distants de 465 km (Paris - Lyon) Quand la cohérence est vérifiée Alors un warning est retourné: """json { "warning": "distance_excessive", "message": "Distance de 465 km entre séquences 2 et 3. Mode 'pieton' inapproprié.", "suggested_mode": "voiture" } """ # Distinction audio-guides vs contenus géolocalisés simples Scénario: Pas de notification 7s avant pour audio-guides multi-séquences Étant donné un audio-guide multi-séquences en mode voiture Et que l'utilisateur approche du prochain point GPS Quand la distance et ETA sont calculés Alors aucun décompte "7→1" n'est déclenché Et le déclenchement se fait au point GPS exact (rayon 30m) Et une notification "Ding" + toast 2s est envoyée Scénario: Notification 7s avant pour contenus géolocalisés simples (1 séquence) Étant donné un contenu géolocalisé simple (1 séquence unique) Et que l'utilisateur approche du point GPS Quand l'ETA devient 7 secondes Alors une notification avec compteur "7→1" est déclenchée Et l'utilisateur doit valider avec bouton "Suivant" # Exception quota pour audio-guides multi-séquences Scénario: Audio-guide multi-séquences compte 1 seul contenu dans quota horaire Étant donné un audio-guide "Visite Safari" avec 12 séquences Et que l'utilisateur a un quota de 0/6 contenus géolocalisés Quand l'utilisateur démarre l'audio-guide (séquence 1) Alors le quota passe à 1/6 Quand l'utilisateur écoute les 12 séquences complètes Alors le quota reste à 1/6 Et toutes les séquences ne consomment PAS 12 quotas Et l'audio-guide entier compte comme 1 seul contenu Scénario: Contenus géolocalisés simples consomment 1 quota chacun Étant donné que l'utilisateur a un quota de 0/6 Quand l'utilisateur accepte un contenu géolocalisé simple "Tour Eiffel" Alors le quota passe à 1/6 Quand l'utilisateur accepte un contenu géolocalisé simple "Arc de Triomphe" Alors le quota passe à 2/6 Quand l'utilisateur accepte un contenu géolocalisé simple "Louvre" Alors le quota passe à 3/6 Et chaque contenu simple consomme 1 quota Scénario: Mixte audio-guides + contenus simples respecte quota 6/h Étant donné que l'utilisateur a un quota de 0/6 Quand l'utilisateur démarre un audio-guide 8 séquences "Safari" Alors le quota passe à 1/6 Quand l'utilisateur accepte 5 contenus géolocalisés simples Alors le quota passe à 6/6 Et le quota horaire est atteint Quand un 7ème contenu est détecté Alors aucune notification n'est envoyée (quota atteint) # Cache et optimisations Scénario: Cache Redis pour calculs GPS fréquents Étant donné que les points GPS d'un audio-guide sont en cache Redis Quand je fais un POST sur "/api/v1/audio-guides/ag_123/check-proximity" Alors les points GPS sont récupérés depuis Redis (pas PostgreSQL) Et le temps de réponse est < 50ms Scénario: Geospatial GEORADIUS Redis pour recherche proximité Étant donné que tous les audio-guides sont indexés dans Redis (GEOADD) Et une position utilisateur (43.1234, 2.5678) Quand je recherche les audio-guides dans un rayon de 5 km Alors Redis GEORADIUS retourne les audio-guides proches Et le temps de réponse est < 20ms # Mise à jour position temps réel Scénario: WebSocket pour mise à jour position en temps réel Étant donné une connexion WebSocket active pour l'audio-guide Quand l'utilisateur envoie sa nouvelle position via WS Alors le serveur calcule immédiatement la proximité Et retourne distance + ETA via WS (pas de polling HTTP) Scénario: Throttling position updates (max 1/seconde) Étant donné que le client envoie des positions GPS toutes les 200ms Quand le serveur reçoit les mises à jour Alors seules les positions espacées de >1 seconde sont traitées Et les autres sont ignorées (throttling) # Cas d'erreur Scénario: Position GPS invalide (coordonnées hors limites) Étant donné une position avec latitude 95.0000 (invalide) Quand je fais un POST sur "/api/v1/audio-guides/{id}/check-proximity" Alors le code HTTP de réponse est 400 Et le message d'erreur est "latitude: doit être entre -90 et 90" Scénario: Audio-guide sans points GPS (mode piéton) Étant donné un audio-guide en mode piéton sans points GPS Quand je fais un POST sur "/api/v1/audio-guides/{id}/check-proximity" Alors le code HTTP de réponse est 400 Et le message d'erreur est "Audio-guide en mode manuel, pas de déclenchement GPS" Scénario: Séquence déjà complétée (skip calcul si utilisateur a déjà passé) Étant donné que l'utilisateur est à la séquence 5 Et qu'il vérifie la proximité du point 3 (déjà écouté) Quand je fais un POST sur "/api/v1/audio-guides/{id}/check-proximity" Alors le calcul n'est pas effectué pour les séquences passées Et le message "Séquence déjà écoutée" est retourné Scénario: Précision GPS insuffisante Étant donné une position avec accuracy ±150m Et un rayon de déclenchement de 30m Quand la précision est vérifiée Alors un warning est retourné: """json { "warning": "low_gps_accuracy", "message": "Précision GPS insuffisante (±150m). Déclenchement automatique peut être perturbé.", "accuracy": 150, "trigger_radius": 30 } """ # Performance Scénario: Optimisation requêtes PostGIS avec index spatial Étant donné que les points GPS ont un index GIST (PostGIS) Quand une requête ST_DWithin est exécutée Alors l'index spatial est utilisé Et le temps d'exécution est < 10ms Scénario: Batch proximity check pour tous les points Étant donné un audio-guide avec 20 séquences Quand je fais un POST sur "/api/v1/audio-guides/{id}/batch-proximity": """json { "user_position": {"latitude": 43.1234, "longitude": 2.5678} } """ Alors toutes les distances sont calculées en une seule requête PostGIS Et le corps de réponse contient: """json { "sequences": [ {"sequence_id": "seq_1", "distance": 0, "in_zone": true}, {"sequence_id": "seq_2", "distance": 150, "in_zone": false}, {"sequence_id": "seq_3", "distance": 350, "in_zone": false} ], "current_sequence": 1, "next_sequence": 2 } """