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,
KaBrandBadgeLe contrat d'intégration (ajouter une app Groupe Ka)
- Adaptateur — comment vos données deviennent des
MapProperty:
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 };
},
};- Thème —
KaMapTheme: accent, familles de pastilles (sale/rent/valuation/highlight× normal/sélection), grappe. - Montage :
<KaMapView theme={theme} adapter={adapter} mapboxToken={TOKEN}
filters={filters} pitch={50} cluster={{ maxZoom: 15 }}>
<KaBrandBadge /> <Tilt3DControl /> <SearchAreaControl />
<LoadingIndicator /> <ResultCount />
<PropertyPreview render={(p) => <MaCarte p={p} />} />
</KaMapView>- 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-imageest une propriété layout (feature-state interdit) : la sélection est réinjectée parsetLayoutProperty. - Grappes : clustering natif, compte + valeur moyenne indicative
(
≈) viaclusterProperties(somme/nombre) —valueClampborne 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/dimmedpar 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. clusterMaxZoomentier obligatoire (sinonInvalid array lengthdans le worker) — arrondi parKaMap.promoteId: undefinedrejeté par la validation Mapbox.getClusterExpansionZoomest à 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) ;buildLayerRegistrymarque 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.setDimmedExceptatté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}.pbfLe 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.