# Ka Maps — framework cartographique de Groupe Ka **Auteur : Simon-Pierre Boucher — contact@spboucher.ai** Ka Maps est le moteur géographique partagé des applications Groupe Ka. Implémenté une fois ici, consommé par **Lou-Ka**, **Immo-Ka** et **Vrai-Prix** — chaque app n'apporte que son **thème** et son **adaptateur de données**. ``` Lou-Ka / Immo-Ka / Vrai-Prix (frontends) │ thème + adaptateur + configuration ▼ Ka Maps ← ce dépôt (~/Desktop/ka-maps) │ ▼ Mapbox GL JS v3 — style Mapbox Standard (3D) ``` ## Empaquetage (décision d'architecture) Trois dépôts séparés → paquet local `@groupe-ka/ka-maps` : | App | Consommation | Pourquoi | |---|---|---| | Lou-Ka (`lou-ka/frontend`) | `file:../../ka-maps` (lien) + `resolve.dedupe` Vite | itération à chaud | | Immo-Ka (`agent-courtage/frontend`) | idem | idem | | Vrai-Prix (Next 16) | `file:../../ka-maps/groupe-ka-ka-maps-0.1.0.tgz` | Turbopack ne résout pas les liens hors racine | Après toute modification du framework : `npm run build` (les apps Vite la voient immédiatement) puis `npm run pack:tarball` + `npm install` dans vrai-prix. ⚠️ Les apps Vite doivent déduper `react`, `react-dom`, `mapbox-gl` (`resolve.dedupe`) et pointer `paths` tsconfig vers **leurs** `@types/react` — sinon double React (crash hooks) et double moteur GL. ## Architecture des sources ``` src/ types/ MapProperty, KaDataAdapter, BBox, KaMapState, GeographicMarketSummary, KaLensStats, HeatmapMetric… core/ KaMap (moteur), KaEventHub (événements centralisés) layers/ propertyLayer (pastilles + grappes), registry (couches par app) services/ BoundsQueryScheduler (debounce, AbortController, cache LRU, livraison monotone — jamais une réponse périmée) styles/ kaBaseStyle (Mapbox Standard + réglages immobiliers), ka-maps.css theming/ KaMapTheme (jetons par app), palettes utils/ format (prix fr-CA), geo (bbox, haversine, GeoJSON), url (caméra partageable), lens (Ka Lens) react/ KaMapView, useKaMap, SearchAreaControl, PropertyPreview, ResultCount, LoadingIndicator, LocateControl, Tilt3DControl, KaBrandBadge ``` ## Le contrat d'intégration (ajouter une app Groupe Ka) 1. **Adaptateur** — comment vos données deviennent des `MapProperty` : ```ts const monAdapter: KaDataAdapter = { id: "mon-app-items", appSource: "mon-app", async fetchInBounds({ bbox, zoom, filters, signal }) { const res = await fetch(`/api/…?bbox=${bboxToString(bbox)}`, { signal }); return { properties: (await res.json()).map(toMapProperty), totalCount }; }, }; ``` 2. **Thème** — `KaMapTheme` : accent, familles de pastilles (`sale`/`rent`/`valuation`/`highlight` × normal/sélection), grappe. 3. **Montage** : ```tsx } /> ``` 4. **Jetons CSS** — sur `.ka-map` : `--ka-accent`, `--ka-surface`, `--ka-ink`, `--ka-line`, `--ka-radius`, `--ka-shadow`, `--ka-font`. ## Rendu des propriétés - **Aucun marqueur DOM** : source GeoJSON + couches symbole/cercle GPU — tenue à 100 000+ points. - **Pastilles de prix** : images canvas 9-slice étirables par famille × état (le 9-slice n'est pas supporté sur les icônes SDF). `icon-image` est une propriété *layout* (feature-state interdit) : la sélection est réinjectée par `setLayoutProperty`. - **Grappes** : clustering natif, compte + **valeur moyenne indicative** (`≈`) via `clusterProperties` (somme/nombre) — `valueClamp` borne la contribution de chaque point (un prix aberrant ne pollue pas la bulle). La médiane exacte n'est pas réductible par supercluster ; elle est disponible côté Ka Lens. - **États** : `hovered`/`selected`/`dimmed` par feature-state (peinture). ## Pièges connus du moteur (payés une fois, documentés ici) - **Polices** : les couches symbole doivent utiliser des polices du serveur de glyphes Mapbox (`DIN Pro …`, `Arial Unicode MS …`). Une police inconnue fait échouer le parsing des tuiles → **source vide, silencieuse**. - **Style à imports (Standard)** : installer les couches sur `load`, config basemap ensuite ; auto-réparation (vérification + reconstruction avec id de source rotatif) intégrée à `KaMap`. - **`clusterMaxZoom` entier** obligatoire (sinon `Invalid array length` dans le worker) — arrondi par `KaMap`. - **`promoteId: undefined`** rejeté par la validation Mapbox. - `getClusterExpansionZoom` est à **callback** (pas une promesse). ## Fond de carte `KA_STYLE_URL` = `mapbox://styles/mapbox/standard` + `applyKaBasemapConfig` : thème `faded` (les prix dominent), POI/transit masqués, `lightPreset` day/dusk = Ka Light/Ka Dark (`setMode`, sans rechargement de style). 3D native (bâtiments, repères) ; inclinaison par défaut 50°, contrôle `Tilt3DControl` (2D/3D). Jeton public `pk.…` fourni par l'app (`mapboxToken`) — jamais de secret serveur dans le navigateur. Attribution Mapbox/OSM repliée en ⓘ (jamais retirée), logo conservé. ## Synchronisation carte ↔ résultats `BoundsQueryScheduler` : `moveend` → debounce → adaptateur → rendu. Mode `manual` : le viewport divergent affiche « Rechercher dans cette zone » (tolérance 15 %) ; mode `auto` : requête à chaque déplacement posé. Annulation `AbortController`, cache LRU (clé bbox+zoom+filtres), livraison strictement monotone. ## Couches, agrégats, Ka Lens (fondations) - `layers/registry.ts` : vocabulaire complet (PROPERTY/MARKET/LAND/ LIFESTYLE/INVESTMENT) ; `buildLayerRegistry` marque ce que chaque app supporte — rien d'autre n'est exposé en production. - `GeographicMarketSummary` : contrat des agrégats par géographie (province → quartier) pour Ka Market Pulse — les API restent à implémenter par app (aucune valeur fictive). - `utils/lens.ts` : `computeLensStats` (médianes, mix de types, parts de baisses/90 j+) sur les propriétés réellement chargées ; `propertiesInBBox` / `propertiesInPolygon` (ray casting) pour la sélection Ka Lens ; `KaMap.setDimmedExcept` atténue le reste. - `HeatmapMetric` : contrat de la future infra heatmap. ## Évolution serveur prévue (architecturé, non déployé) ``` SQLite (bbox + index (lat,lng)) ← aujourd'hui, les 3 apps PostgreSQL + PostGIS (GIST, ST_Intersects) ← quand la volumétrie l'exige Martin → tuiles vectorielles MVT → CDN ← /api/map/…/{z}/{x}/{y}.pbf ``` Le client est prêt : remplacer la source GeoJSON par une source `vector` + `source-layer` ne touche ni les couches ni les apps. ## Commandes `npm run build` · `npm run typecheck` · `npm test` (30 tests : formats fr-CA, bbox/URL, scheduler — debounce/annulation/cache/monotonie —, Ka Lens) · `npm run pack:tarball`.