From bccde64f9319ec241425a55824ffceb0289c8ea2 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 13:29:58 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20CodeRabbit=20Chat:=20Implement?= =?UTF-8?q?=20requested=20code=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/map/geojson-layer.tsx | 75 ++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/components/map/geojson-layer.tsx b/components/map/geojson-layer.tsx index 63ac4e50..33ad29b4 100644 --- a/components/map/geojson-layer.tsx +++ b/components/map/geojson-layer.tsx @@ -1,7 +1,7 @@ 'use client' -import { useEffect } from 'react' -import type mapboxgl from 'mapbox-gl' +import { useEffect, useRef } from 'react' +import mapboxgl from 'mapbox-gl' import { useMap } from './map-context' import type { FeatureCollection } from 'geojson' @@ -12,6 +12,7 @@ interface GeoJsonLayerProps { export function GeoJsonLayer({ id, data }: GeoJsonLayerProps) { const { map } = useMap() + const popupRef = useRef(null) useEffect(() => { if (!map || !data) return @@ -20,6 +21,7 @@ export function GeoJsonLayer({ id, data }: GeoJsonLayerProps) { const pointLayerId = `geojson-point-layer-${id}` const polygonLayerId = `geojson-polygon-layer-${id}` const polygonOutlineLayerId = `geojson-polygon-outline-layer-${id}` + const labelLayerId = `geojson-label-layer-${id}` const onMapLoad = () => { // Add source if it doesn't exist @@ -77,6 +79,68 @@ export function GeoJsonLayer({ id, data }: GeoJsonLayerProps) { } }) } + + // Add persistent text label layer for all feature types + if (!map.getLayer(labelLayerId)) { + map.addLayer({ + id: labelLayerId, + type: 'symbol', + source: sourceId, + layout: { + 'text-field': ['get', 'name'], + 'text-size': 11, + 'text-anchor': 'top', + 'text-offset': [0, 0.8], + 'text-max-width': 12, + 'text-allow-overlap': false, + 'text-ignore-placement': false, + }, + paint: { + 'text-color': '#ffffff', + 'text-halo-color': '#000000', + 'text-halo-width': 1.5, + } + }) + } + + // Show popup with name + description on click (points and polygons) + const handleFeatureClick = (e: mapboxgl.MapMouseEvent & { features?: mapboxgl.MapboxGeoJSONFeature[] }) => { + const feature = e.features?.[0] + if (!feature) return + + const props = feature.properties as { name?: string; description?: string } | null + if (!props?.name) return + + const coordinates = e.lngLat + + const html = ` +
+ ${props.name} + ${props.description ? `${props.description}` : ''} +
+ ` + + if (popupRef.current) { + popupRef.current.remove() + } + + popupRef.current = new mapboxgl.Popup({ closeButton: true, maxWidth: '260px' }) + .setLngLat(coordinates) + .setHTML(html) + .addTo(map) + } + + map.on('click', pointLayerId, handleFeatureClick) + map.on('click', polygonLayerId, handleFeatureClick) + + // Pointer cursor on hover + const setCursorPointer = () => { map.getCanvas().style.cursor = 'pointer' } + const setCursorDefault = () => { map.getCanvas().style.cursor = '' } + + map.on('mouseenter', pointLayerId, setCursorPointer) + map.on('mouseleave', pointLayerId, setCursorDefault) + map.on('mouseenter', polygonLayerId, setCursorPointer) + map.on('mouseleave', polygonLayerId, setCursorDefault) } if (map.isStyleLoaded()) { @@ -87,11 +151,16 @@ export function GeoJsonLayer({ id, data }: GeoJsonLayerProps) { // Cleanup function return () => { + if (popupRef.current) { + popupRef.current.remove() + popupRef.current = null + } if (map.isStyleLoaded()) { + if (map.getLayer(labelLayerId)) map.removeLayer(labelLayerId) if (map.getLayer(pointLayerId)) map.removeLayer(pointLayerId) if (map.getLayer(polygonLayerId)) map.removeLayer(polygonLayerId) if (map.getLayer(polygonOutlineLayerId)) map.removeLayer(polygonOutlineLayerId) - if (map.getSource(sourceId)) map.removeSource(sourceId) + } } }, [map, id, data])