Ka Maps — Synchronisation liste ↔ carte (le modèle Groupe-KA)
Référence du module « recherche synchronisée » introduit avec Lou-Ka
(frontend/src/search/MapSearch.tsx), conçu pour être décliné tel quel sur
immo-ka, resto-ka, sorti-ka et servir de contrat à l'app iOS KA.
Le principe : une seule vérité partagée
La liste et la carte sont deux vues du même état de recherche :
État = { filtres, zone (bbox visible OU polygone dessiné), tri, page }Chaque changement d'état déclenche UNE requête vers l'endpoint unifié
(/api/search chez Lou-Ka) qui renvoie dans la même réponse :
| Champ | Rôle |
|---|---|
total |
compteur partagé — affiché en tête de liste, égal PAR CONSTRUCTION au nombre de points carte |
listings |
la page de liste demandée (objets complets) |
points |
TOUS les points carte au format compact [uid, lng, lat, prix, verdict], triés comme la liste |
unpositioned |
annonces filtrées sans coordonnées (affichées honnêtement, jamais silencieusement perdues) |
Corollaires structurels (pas des efforts de synchronisation, des invariants) :
- compteur liste = nombre de marqueurs, dans 100 % des cas ;
index d'un uid dans points÷page_size= sa page de liste → un clic sur n'importe quel marqueur peut TOUJOURS faire défiler la liste vers l'annonce, même si elle est sur une autre page ;- aucune requête périmée ne peut écraser une récente (numéro de séquence +
AbortController; le serveur n'est jamais la source du tri d'arrivée).
Répartition des rôles
Ka Maps (framework) fournit :
KaMap.fitBounds(bbox)/fitToProperties()— recadrages animés marqués programmés ;moveend { byUser }— distinction geste utilisateur / mouvement du code, c'est la clé du « respect de l'intention » : seuls les gestes verrouillent la vue (userLocked) et déclenchent la recherche par zone ;- sélection & survol bidirectionnels par feature-state GPU
(
select(uid, "map"|"app"),setHovered, événementsselect/hover) ; - outil polygone intégré (
startDraw,clearDrawnPolygon,setDrawnPolygon, événementdraw) +<DrawControl/>; - fourchette de prix des clusters au survol (
clusterHover, accumulateursvalueMin/valueMax) ; - état « vu » (
setSeenIds) — pastilles atténuées des annonces consultées ; - utilitaires :
bboxOfProperties,pointInPolygon,bboxToString,cameraToParams/cameraFromParams.
L'app possède : l'état de recherche, la requête unifiée, l'URL, la liste, le carrousel mobile et le langage visuel des cartes/mini-fiches.
La machine d'états côté app (copier ce comportement)
- Arrivée sans caméra dans l'URL : requête sans zone →
fitBoundsanimé sur les résultats (byUser: false, ne verrouille pas la vue). - Arrivée avec caméra (lien partagé) : la zone visible restaurée devient la contrainte spatiale de la première requête ; vue considérée verrouillée.
- Geste utilisateur (
byUser: true) : verrouille la vue ; si « Rechercher quand je déplace la carte » (défaut : coché) → nouvelle requête avec la bbox (debounce ~250 ms après la fin du geste) ; sinon marquer la zone divergée et montrer « Rechercher dans cette zone ». - Filtre modifié : page 1 ; si la vue n'est PAS verrouillée → requête sans zone + fitBounds ; si verrouillée → requête dans la zone courante et bouton discret « Recadrer sur les résultats ».
- Polygone dessiné : remplace la bbox (il EST la zone), devient une puce
retirable, encodé dans l'URL (
zone=lng,lat;…), fitBounds sur son contenu. - Tri/page : même requête ; un changement de page passe
include=liste(les points, identiques, ne sont pas retéléchargés). - URL : caméra (
lat/lng/zoom) +tri+page+zone+move=0viareplaceState— partager le lien reproduit la recherche à l'identique.
Miroirs (latence < 100 ms)
- survol carte d'annonce →
setHovered(uid, "app")(feature-state, aucun re-rendu carte) ; - survol marqueur → événement
hover→ classe CSS sur la carte d'annonce + indicateur « ▲/▼ annonce hors écran » si elle n'est pas visible ; - clic marqueur →
select→ page ajustée au besoin → défilement animé + pulsation ; - clic annonce →
select(uid, "app")→ recentrage doux (uniquement si hors champ) + mini-fiche ; Cmd/Ctrl-clic ou 2ᵉ clic → navigation vers la fiche.
Mobile
Bascule Liste ↔ Carte sans perte (état dans l'URL). En vue carte : carrousel
horizontal scroll-snap en bas, alimenté par la MÊME page de liste ;
balayage → sélection du marqueur (select, origin "app"), marqueur tapé →
défilement du carrousel. Un seul état, deux projections.
Anti-régression
- E2E :
lou-ka/scripts/test-sync.mjs(25 vérifications, critères 1-9). - Unitaires framework :
ka-maps/tests/drawGeo.test.ts(+ suites existantes). - API :
lou-ka/tests/test_search.py(règle d'or, tris, polygone, bornes).