SPB Git forge

spb/ka-maps

Public
6commits 1branches 0releases
448.0 KBsize
maindefault branch
29 days agolast push
TypeScript 87.7% CSS 12.3%
7.0 KB

# 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.

text
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

text
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 };
  },
};
  1. Thème — KaMapTheme : accent, familles de pastilles (sale/rent/valuation/highlight × normal/sélection), grappe.
  2. Montage :
tsx
<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>
  1. 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é)

text
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.