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 · 165 lines markdown
Rendered Raw Blame History
1# Ka Maps — framework cartographique de Groupe Ka23**Auteur : Simon-Pierre Boucher — contact@spboucher.ai**45Ka Maps est le moteur géographique partagé des applications Groupe Ka.6Implémenté une fois ici, consommé par **Lou-Ka**, **Immo-Ka** et7**Vrai-Prix** — chaque app n'apporte que son **thème** et son8**adaptateur de données**.910```11Lou-Ka / Immo-Ka / Vrai-Prix (frontends)12            │  thème + adaptateur + configuration13            ▼14         Ka Maps          ← ce dépôt (~/Desktop/ka-maps)15            │16            ▼17      Mapbox GL JS v3 — style Mapbox Standard (3D)18```1920## Empaquetage (décision d'architecture)2122Trois dépôts séparés → paquet local `@groupe-ka/ka-maps` :2324| App | Consommation | Pourquoi |25|---|---|---|26| Lou-Ka (`lou-ka/frontend`) | `file:../../ka-maps` (lien) + `resolve.dedupe` Vite | itération à chaud |27| Immo-Ka (`agent-courtage/frontend`) | idem | idem |28| Vrai-Prix (Next 16) | `file:../../ka-maps/groupe-ka-ka-maps-0.1.0.tgz` | Turbopack ne résout pas les liens hors racine |2930Après toute modification du framework :31`npm run build` (les apps Vite la voient immédiatement) puis32`npm run pack:tarball` + `npm install` dans vrai-prix.3334⚠️ Les apps Vite doivent déduper `react`, `react-dom`, `mapbox-gl`35(`resolve.dedupe`) et pointer `paths` tsconfig vers **leurs**36`@types/react` — sinon double React (crash hooks) et double moteur GL.3738## Architecture des sources3940```41src/42  types/       MapProperty, KaDataAdapter, BBox, KaMapState,43               GeographicMarketSummary, KaLensStats, HeatmapMetric…44  core/        KaMap (moteur), KaEventHub (événements centralisés)45  layers/      propertyLayer (pastilles + grappes), registry (couches par app)46  services/    BoundsQueryScheduler (debounce, AbortController, cache LRU,47               livraison monotone — jamais une réponse périmée)48  styles/      kaBaseStyle (Mapbox Standard + réglages immobiliers), ka-maps.css49  theming/     KaMapTheme (jetons par app), palettes50  utils/       format (prix fr-CA), geo (bbox, haversine, GeoJSON),51               url (caméra partageable), lens (Ka Lens)52  react/       KaMapView, useKaMap, SearchAreaControl, PropertyPreview,53               ResultCount, LoadingIndicator, LocateControl, Tilt3DControl,54               KaBrandBadge55```5657## Le contrat d'intégration (ajouter une app Groupe Ka)58591. **Adaptateur** — comment vos données deviennent des `MapProperty` :6061```ts62const monAdapter: KaDataAdapter = {63  id: "mon-app-items",64  appSource: "mon-app",65  async fetchInBounds({ bbox, zoom, filters, signal }) {66    const res = await fetch(`/api/…?bbox=${bboxToString(bbox)}`, { signal });67    return { properties: (await res.json()).map(toMapProperty), totalCount };68  },69};70```71722. **Thème** — `KaMapTheme` : accent, familles de pastilles73   (`sale`/`rent`/`valuation`/`highlight` × normal/sélection), grappe.743. **Montage** :7576```tsx77<KaMapView theme={theme} adapter={adapter} mapboxToken={TOKEN}78           filters={filters} pitch={50} cluster={{ maxZoom: 15 }}>79  <KaBrandBadge /> <Tilt3DControl /> <SearchAreaControl />80  <LoadingIndicator /> <ResultCount />81  <PropertyPreview render={(p) => <MaCarte p={p} />} />82</KaMapView>83```84854. **Jetons CSS** — sur `.ka-map` : `--ka-accent`, `--ka-surface`,86   `--ka-ink`, `--ka-line`, `--ka-radius`, `--ka-shadow`, `--ka-font`.8788## Rendu des propriétés8990- **Aucun marqueur DOM** : source GeoJSON + couches symbole/cercle GPU —91  tenue à 100 000+ points.92- **Pastilles de prix** : images canvas 9-slice étirables par famille ×93  état (le 9-slice n'est pas supporté sur les icônes SDF). `icon-image`94  est une propriété *layout* (feature-state interdit) : la sélection est95  réinjectée par `setLayoutProperty`.96- **Grappes** : clustering natif, compte + **valeur moyenne indicative**97  (`≈`) via `clusterProperties` (somme/nombre) — `valueClamp` borne la98  contribution de chaque point (un prix aberrant ne pollue pas la bulle).99  La médiane exacte n'est pas réductible par supercluster ; elle est100  disponible côté Ka Lens.101- **États** : `hovered`/`selected`/`dimmed` par feature-state (peinture).102103## Pièges connus du moteur (payés une fois, documentés ici)104105- **Polices** : les couches symbole doivent utiliser des polices du106  serveur de glyphes Mapbox (`DIN Pro …`, `Arial Unicode MS …`). Une107  police inconnue fait échouer le parsing des tuiles → **source vide,108  silencieuse**.109- **Style à imports (Standard)** : installer les couches sur `load`,110  config basemap ensuite ; auto-réparation (vérification + reconstruction111  avec id de source rotatif) intégrée à `KaMap`.112- **`clusterMaxZoom` entier** obligatoire (sinon `Invalid array length`113  dans le worker) — arrondi par `KaMap`.114- **`promoteId: undefined`** rejeté par la validation Mapbox.115- `getClusterExpansionZoom` est à **callback** (pas une promesse).116117## Fond de carte118119`KA_STYLE_URL` = `mapbox://styles/mapbox/standard` +120`applyKaBasemapConfig` : thème `faded` (les prix dominent), POI/transit121masqués, `lightPreset` day/dusk = Ka Light/Ka Dark (`setMode`, sans122rechargement de style). 3D native (bâtiments, repères) ; inclinaison par123défaut 50°, contrôle `Tilt3DControl` (2D/3D). Jeton public `pk.…` fourni124par l'app (`mapboxToken`) — jamais de secret serveur dans le navigateur.125Attribution Mapbox/OSM repliée en ⓘ (jamais retirée), logo conservé.126127## Synchronisation carte ↔ résultats128129`BoundsQueryScheduler` : `moveend` → debounce → adaptateur → rendu.130Mode `manual` : le viewport divergent affiche « Rechercher dans cette131zone » (tolérance 15 %) ; mode `auto` : requête à chaque déplacement132posé. Annulation `AbortController`, cache LRU (clé bbox+zoom+filtres),133livraison strictement monotone.134135## Couches, agrégats, Ka Lens (fondations)136137- `layers/registry.ts` : vocabulaire complet (PROPERTY/MARKET/LAND/138  LIFESTYLE/INVESTMENT) ; `buildLayerRegistry` marque ce que chaque app139  supporte — rien d'autre n'est exposé en production.140- `GeographicMarketSummary` : contrat des agrégats par géographie141  (province → quartier) pour Ka Market Pulse — les API restent à142  implémenter par app (aucune valeur fictive).143- `utils/lens.ts` : `computeLensStats` (médianes, mix de types, parts de144  baisses/90 j+) sur les propriétés réellement chargées ;145  `propertiesInBBox` / `propertiesInPolygon` (ray casting) pour la146  sélection Ka Lens ; `KaMap.setDimmedExcept` atténue le reste.147- `HeatmapMetric` : contrat de la future infra heatmap.148149## Évolution serveur prévue (architecturé, non déployé)150151```152SQLite (bbox + index (lat,lng))            ← aujourd'hui, les 3 apps153PostgreSQL + PostGIS (GIST, ST_Intersects) ← quand la volumétrie l'exige154Martin → tuiles vectorielles MVT → CDN     ← /api/map/…/{z}/{x}/{y}.pbf155```156157Le client est prêt : remplacer la source GeoJSON par une source158`vector` + `source-layer` ne touche ni les couches ni les apps.159160## Commandes161162`npm run build` · `npm run typecheck` · `npm test` (30 tests : formats163fr-CA, bbox/URL, scheduler — debounce/annulation/cache/monotonie —, Ka164Lens) · `npm run pack:tarball`.165