// ----------------------------------------------------------------------------- // Author: Simon-Pierre Boucher // Contact: contact@spboucher.ai // Project: Toit-Ka // components/PropertyMap.tsx : mini-carte d'une annonce (fiche détail). // Fond Ka Maps (Mapbox Standard 3D), anneaux de marchabilité 5/15 min et // marqueur-prix pulsant — couleur d'anneaux selon l'univers. // ----------------------------------------------------------------------------- import { useEffect, useRef } from "react"; import mapboxgl from "mapbox-gl"; import "mapbox-gl/dist/mapbox-gl.css"; import { applyKaBasemapConfig, KA_STYLE_URL } from "@groupe-ka/ka-maps"; import { formatCompactPrice } from "@groupe-ka/ka-maps"; import { MAPBOX_TOKEN } from "../kamaps/config"; /** Cercle géodésique approché (72 côtés) pour les anneaux de marche. */ function circle(lng: number, lat: number, radiusM: number): GeoJSON.Feature { const pts: [number, number][] = []; for (let i = 0; i <= 72; i++) { const a = (i / 72) * 2 * Math.PI; const dLat = (radiusM * Math.cos(a)) / 111_000; const dLng = (radiusM * Math.sin(a)) / (111_000 * Math.cos((lat * Math.PI) / 180)); pts.push([lng + dLng, lat + dLat]); } return { type: "Feature", geometry: { type: "Polygon", coordinates: [pts] }, properties: {} }; } const RING_COLORS: Record = { louer: ["#1c5c41", "#123f2e"], acheter: ["#e23744", "#b3202b"], }; export default function PropertyMap({ lat, lng, price, tx }: { lat: number; lng: number; price?: number | null; tx: "louer" | "acheter"; }) { const div = useRef(null); useEffect(() => { if (!div.current) return; const [fill, line] = RING_COLORS[tx] ?? RING_COLORS.acheter; const map = new mapboxgl.Map({ container: div.current, accessToken: MAPBOX_TOKEN, style: KA_STYLE_URL, center: [lng, lat], zoom: 15.2, pitch: 45, scrollZoom: false, dragRotate: false, attributionControl: false, }); map.addControl(new mapboxgl.AttributionControl({ compact: true }), "bottom-right"); map.addControl(new mapboxgl.NavigationControl({ showCompass: false }), "top-right"); map.touchZoomRotate.disableRotation(); map.once("load", () => { applyKaBasemapConfig(map, "light"); map.addSource("rings", { type: "geojson", data: { type: "FeatureCollection", features: [circle(lng, lat, 400), circle(lng, lat, 1200)], }, }); map.addLayer({ id: "rings-fill", type: "fill", source: "rings", slot: "top", paint: { "fill-color": fill, "fill-opacity": 0.035 }, }); map.addLayer({ id: "rings-line", type: "line", source: "rings", slot: "top", paint: { "line-color": line, "line-opacity": 0.55, "line-width": 1.4, "line-dasharray": [3, 3], }, }); }); const el = document.createElement("div"); el.className = "pm-marker"; el.innerHTML = `
${price != null ? formatCompactPrice(price) : "•"}
` + `
`; const marker = new mapboxgl.Marker({ element: el, anchor: "bottom" }) .setLngLat([lng, lat]) .addTo(map); return () => { marker.remove(); map.remove(); }; }, [lat, lng, price, tx]); return (
); }