// ----------------------------------------------------------------------------- // Immo-Ka — Agrégateur de propriétés à vendre (province de Québec) // Auteur : Simon-Pierre Boucher — contact@spboucher.ai // components/PropertyMap.tsx : carte d'emplacement de la fiche propriété // · centrée et zoomée sur LA propriété (fond « Groupe Ka » maison) // · marqueur pulsant + pastille de prix, rouge si sous l'estimation Vrai-Prix // · anneaux « 5 min / 15 min à pied » — signature visuelle Immo-Ka // ----------------------------------------------------------------------------- import { useEffect, useRef } from "react"; import maplibregl from "maplibre-gl"; import "maplibre-gl/dist/maplibre-gl.css"; import immokaStyle from "../map/immokaStyle"; import { shortPrice } from "../map/pills"; /** Cercle géodésique approx. (polygone 72 côtés) autour d'un point. */ function circle(lng: number, lat: number, radiusM: number): GeoJSON.Feature { const pts: [number, number][] = []; const dLat = radiusM / 111_000; const dLng = radiusM / (111_000 * Math.cos((lat * Math.PI) / 180)); for (let i = 0; i <= 72; i++) { const a = (i / 72) * 2 * Math.PI; pts.push([lng + dLng * Math.cos(a), lat + dLat * Math.sin(a)]); } return { type: "Feature", properties: {}, geometry: { type: "Polygon", coordinates: [pts] } }; } interface Props { lat: number; lng: number; price?: number | null; deal?: boolean; // prix sous l'estimation Vrai-Prix } export default function PropertyMap({ lat, lng, price, deal }: Props) { const div = useRef(null); useEffect(() => { if (!div.current) return; const map = new maplibregl.Map({ container: div.current, style: immokaStyle, center: [lng, lat], zoom: 14.6, attributionControl: false, scrollZoom: false, // ne pas piéger le défilement de la page dragRotate: false, }); map.addControl(new maplibregl.NavigationControl({ showCompass: false }), "top-right"); map.touchZoomRotate.disableRotation(); map.on("load", () => { // anneaux piéton — 400 m ≈ 5 min, 1,2 km ≈ 15 min 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", paint: { "fill-color": "#e23744", "fill-opacity": 0.035 }, }); map.addLayer({ id: "rings-line", type: "line", source: "rings", paint: { "line-color": "#b3202b", "line-opacity": 0.55, "line-width": 1.4, "line-dasharray": [3, 3] }, }); }); // marqueur maison : halo pulsant + pastille de prix const el = document.createElement("div"); el.className = "pm-marker"; el.innerHTML = `
${shortPrice(price)}${deal ? " ↓" : ""}
`; const marker = new maplibregl.Marker({ element: el, anchor: "bottom" }) .setLngLat([lng, lat]).addTo(map); return () => { marker.remove(); map.remove(); }; }, [lat, lng, price, deal]); return (
); }