← Phase 1 Overview / Carte & Lieux
🗺️
Module 3 — Phase 1 MVP

Carte & Lieux

Carte interactive Mapbox avec pins colorés selon la sécurité, fiches lieux détaillées, recherche géospatiale et score de risque temps réel.

Backlog

User Stories

US3.1
En tant qu'utilisateur, je vois une carte plein écran avec les lieux populaires sous forme de pins
must 5 pts
US3.2
En tant qu'utilisateur, les pins sont colorés selon le score de sécurité (vert/jaune/orange/rouge)
must 4 pts
US3.3
En tant qu'utilisateur, je peux taper sur un pin pour voir la fiche lieu (aperçu rapide)
must 3 pts
US3.4
En tant qu'utilisateur, la fiche lieu complète affiche : photo, score sécu, prix moyens, top posts, food
must 8 pts
US3.5
En tant qu'utilisateur, je peux rechercher un lieu par nom depuis la carte
must 4 pts
US3.6
En tant qu'utilisateur, je peux filtrer les pins par type (food, activité, hébergement, sécurité)
must 3 pts
US3.7
En tant qu'utilisateur, je peux voir les lieux proches de ma position GPS actuelle
must 4 pts
US3.8
En tant qu'utilisateur, la carte gère le clustering de pins (performance >100 pins)
must 5 pts
US3.9
En tant qu'utilisateur, je peux ajouter un nouveau lieu s'il n'existe pas (suggestion)
should 4 pts
US3.10
En tant qu'utilisateur, je peux sauvegarder un lieu dans mes "favoris"
should 2 pts
US3.11
En tant que système, le score de sécurité d'un lieu est mis à jour automatiquement depuis les alertes actives
must 8 pts
REST API /places

Endpoints API

GET /places/nearby ?lat=&lng=&radius=km&type=&limit=50 public
GET /places/:id Fiche lieu complète public
GET /places/:id/posts Posts liés à un lieu (paginé) public
GET /places/:id/safety Score + alertes actives sur ce lieu public
GET /places/search ?q=&country=&type= (Typesense) public
POST /places Suggérer un nouveau lieu (modération) 🔒 auth
POST /places/:id/save Sauvegarder en favoris 🔒 auth
DELETE /places/:id/save Retirer des favoris 🔒 auth
GET /places/map-data Pins simplifiés pour la carte (?bounds=sw_lat,sw_lng,ne_lat,ne_lng) public
GET /users/me/saved-places Lieux favoris (paginé) 🔒 auth
Schémas de données

Tables Géospatiales

TABLE places
id UUID PRIMARY KEY name TEXT NOT NULL name_translations JSONB -- {fr, en, es, ...} type ENUM('city','country','neighborhood','venue','food_spot','attraction') location GEOMETRY(Point, 4326) -- PostGIS lat DECIMAL(10,7) -- redondant pour perf lng DECIMAL(10,7) country_code CHAR(2) region TEXT safety_score FLOAT DEFAULT 75.0 -- 0-100 safety_level INT -- 1=safe 2=caution 3=risky 4=danger 5=critical safety_updated_at TIMESTAMPTZ avg_cost_usd DECIMAL(8,2) cover_photo_url TEXT description TEXT tags TEXT[] posts_count INT DEFAULT 0 status ENUM('active','pending','rejected') DEFAULT 'active' created_at TIMESTAMPTZ DEFAULT NOW() INDEX: USING GIST (location)
TABLE place_saves
user_id UUID REFERENCES users(id) place_id UUID REFERENCES places(id) created_at TIMESTAMPTZ DEFAULT NOW() PRIMARY KEY (user_id, place_id)
TABLE place_suggestions
id UUID PRIMARY KEY suggested_by UUID REFERENCES users(id) name TEXT lat DECIMAL(10,7) lng DECIMAL(10,7) type TEXT description TEXT status ENUM('pending','approved','rejected') DEFAULT 'pending' created_at TIMESTAMPTZ DEFAULT NOW()
Business Logic

Règles Métier & Performance

🗺️
Mapbox SDK React Native
Utilisation de MapboxGL. Style custom NOMIIQ (bleu nuit pour l'eau, beige clair pour le terrain). Tiles servis via Cloudflare pour cache edge.
📍
Endpoint /places/map-data optimisé
Retourne seulement id, lat, lng, safety_level, type (< 1KB/pin). Clustering côté client (supercluster). Max 500 pins par viewport.
🔢
Score sécurité calculé
= 0.5×alertes_actives_poids + 0.3×score_pays_base + 0.2×signalements_communautaires. Recalculé à chaque nouvelle alerte sur la zone.
🔍
Recherche Typesense
Index places avec champs : name, tags, country, description. Tolérance fautes de frappe 2. Résultats en < 50ms.
📐
PostGIS ST_DWithin
Requête places_nearby utilise ST_DWithin(location, ST_SetSRID(ST_Point(lng,lat),4326), radius_meters). Index GIST obligatoire.
🔄
Cache carte Redis
Les données de carte par bounding box sont cachées 5 min dans Redis. Invalidation sélective si une alerte sécurité se déclenche sur la zone.
Critères d'acceptation
Carte charge en < 2s avec 200 pins visibles (connexion 4G)
Clustering fonctionnel : groupes de pins visibles dès 20+ éléments proches
Fiche lieu charge en < 1s depuis le cache
Recherche retourne des résultats en < 200ms
Score sécurité mis à jour dans < 5 min après une nouvelle alerte
GPS activé : les lieux proches (< 5km) apparaissent en premier