-
-
Notifications
You must be signed in to change notification settings - Fork 7
Add Mapbox Compare Slider to Drawing Mode #454
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,7 +1,8 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'use client' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useEffect, useRef, useCallback } from 'react' // Removed useState | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useEffect, useRef, useCallback, useState } from 'react' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import mapboxgl from 'mapbox-gl' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'mapbox-gl-compare/dist/mapbox-gl-compare.css' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import MapboxDraw from '@mapbox/mapbox-gl-draw' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import * as turf from '@turf/turf' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { toast } from 'react-toastify' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -17,7 +18,11 @@ mapboxgl.accessToken = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN as string; | |||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const Mapbox: React.FC<{ position?: { latitude: number; longitude: number; } }> = ({ position }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mapContainer = useRef<HTMLDivElement>(null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const afterMapContainer = useRef<HTMLDivElement>(null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const map = useRef<mapboxgl.Map | null>(null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const afterMap = useRef<mapboxgl.Map | null>(null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const compareRef = useRef<any>(null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const afterMapMarkersRef = useRef<mapboxgl.Marker[]>([]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { setMap } = useMap() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const drawRef = useRef<MapboxDraw | null>(null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const rotationFrameRef = useRef<number | null>(null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -61,6 +66,107 @@ export const Mapbox: React.FC<{ position?: { latitude: number; longitude: number | |||||||||||||||||||||||||||||||||||||||||||||||||||
| }, []) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Create measurement labels for all features | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const syncDrawingsToAfterMap = useCallback(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!afterMap.current || !afterMap.current.getSource('draw-mirror')) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const source = afterMap.current.getSource('draw-mirror') as mapboxgl.GeoJSONSource; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const features = mapData.drawnFeatures?.map(df => df.geometry) || []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| source.setData({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'FeatureCollection', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| features: features.map((g, i) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'Feature', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| geometry: g, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| properties: mapData.drawnFeatures?.[i] || {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| })) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+69
to
+83
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mirroring features is currently built by pairing SuggestionBuild the mirrored FeatureCollection directly from const features = (mapData.drawnFeatures ?? []).map(df => ({
type: 'Feature' as const,
geometry: df.geometry,
properties: {
id: df.id,
measurement: df.measurement,
featureType: df.type,
},
}))
source.setData({ type: 'FeatureCollection', features })Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Mirror labels | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMapMarkersRef.current.forEach(m => m.remove()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMapMarkersRef.current = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| mapData.drawnFeatures?.forEach(feature => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| let coords: [number, number] | null = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (feature.type === 'Polygon') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const centroid = turf.centroid(feature.geometry); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| coords = centroid.geometry.coordinates as [number, number]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (feature.type === 'LineString') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const line = feature.geometry.coordinates; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const midIndex = Math.floor(line.length / 2) - 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| coords = (midIndex >= 0 ? line[midIndex] : line[0]) as [number, number]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+93
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For SuggestionUse the true midpoint vertex (or a proper along-line midpoint) for more stable placement. Simple vertex midpoint: const midIndex = Math.floor(line.length / 2)
coords = line[midIndex] as [number, number]More accurate geometric midpoint: const ls = turf.lineString(line)
const len = turf.length(ls)
const mid = turf.along(ls, len / 2)
coords = mid.geometry.coordinates as [number, number]Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.
Comment on lines
+92
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The More importantly, this duplicates label placement logic already present in SuggestionFix midpoint math and centralize label coordinate selection so both maps use the same function. Example: const getLabelCoord = (f: DrawnFeature): [number, number] | null => {
if (f.type === 'Polygon') {
const c = turf.centroid(f.geometry).geometry.coordinates
return c as [number, number]
}
if (f.type === 'LineString') {
const coords = f.geometry.coordinates
const mid = Math.floor((coords.length - 1) / 2)
return (coords[mid] ?? coords[0]) as [number, number]
}
return null
}Then use it for both the primary labels and the mirrored labels to avoid drift. Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (coords && afterMap.current) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const el = document.createElement('div'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| el.className = feature.type === 'Polygon' ? 'area-label' : 'distance-label'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| el.style.background = 'rgba(255, 255, 255, 0.8)' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| el.style.padding = '4px 8px' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| el.style.borderRadius = '4px' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| el.style.fontSize = '12px' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| el.style.fontWeight = 'bold' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| el.style.color = '#333333' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| el.style.boxShadow = '0 2px 4px rgba(0,0,0,0.2)' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| el.style.pointerEvents = 'none' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| el.textContent = feature.measurement; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const marker = new mapboxgl.Marker({ element: el }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| .setLngLat(coords) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| .addTo(afterMap.current); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMapMarkersRef.current.push(marker); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [mapData.drawnFeatures]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const setupAfterMapLayers = useCallback(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!afterMap.current) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Add source for mirrored drawings | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMap.current.addSource('draw-mirror', { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'geojson', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'FeatureCollection', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| features: [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+120
to
+130
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard against duplicate source/layer creation. If 🐛 Proposed fix const setupAfterMapLayers = useCallback(() => {
if (!afterMap.current) return;
+ // Avoid duplicate source/layer creation
+ if (afterMap.current.getSource('draw-mirror')) return;
+
// Add source for mirrored drawings
afterMap.current.addSource('draw-mirror', {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Add layers for polygons and lines | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMap.current.addLayer({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: 'draw-mirror-polygons', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'fill', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| source: 'draw-mirror', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| filter: ['==', '$type', 'Polygon'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| paint: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'fill-color': '#fbb03b', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'fill-opacity': 0.1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMap.current.addLayer({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: 'draw-mirror-polygons-outline', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'line', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| source: 'draw-mirror', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| filter: ['==', '$type', 'Polygon'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| paint: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'line-color': '#fbb03b', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'line-width': 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMap.current.addLayer({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: 'draw-mirror-lines', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'line', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| source: 'draw-mirror', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| filter: ['==', '$type', 'LineString'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| paint: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'line-color': '#fbb03b', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'line-width': 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Initial sync | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| syncDrawingsToAfterMap(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [syncDrawingsToAfterMap]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const updateMeasurementLabels = useCallback(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!map.current || !drawRef.current) return | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -335,6 +441,79 @@ export const Mapbox: React.FC<{ position?: { latitude: number; longitude: number | |||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [setMapData]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Handle comparison map initialization | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (mapType === MapToggleEnum.DrawingMode && map.current && afterMapContainer.current && !afterMap.current) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const center = map.current.getCenter(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const zoom = map.current.getZoom(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const pitch = map.current.getPitch(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const bearing = map.current.getBearing(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMap.current = new mapboxgl.Map({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| container: afterMapContainer.current, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| style: 'mapbox://styles/mapbox/streets-v12', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| center: [center.lng, center.lat], | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| zoom: zoom, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| pitch: pitch, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| bearing: bearing, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| maxZoom: 22, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| attributionControl: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMap.current.on('load', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!map.current || !afterMap.current || !afterMapContainer.current?.parentElement) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Late-import mapbox-gl-compare to avoid SSR issues | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const CompareModule = require('mapbox-gl-compare'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const CompareConstructor = CompareModule.default || CompareModule; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Create the compare control | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| compareRef.current = new CompareConstructor( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| map.current, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMap.current, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMapContainer.current.parentElement, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| orientation: 'vertical', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| mousemove: false | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Setup layers on afterMap | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| setupAfterMapLayers(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error('Error initializing mapbox-gl-compare:', error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Cleanup when leaving DrawingMode | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (mapType !== MapToggleEnum.DrawingMode) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (compareRef.current) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| compareRef.current.remove(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| compareRef.current = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (afterMap.current) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMap.current.remove(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMap.current = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMapMarkersRef.current.forEach(m => m.remove()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMapMarkersRef.current = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (map.current) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const container = map.current.getContainer(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (container) container.style.clip = ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [mapType, setupAfterMapLayers]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Effect to sync drawings when they change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (mapType === MapToggleEnum.DrawingMode) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| syncDrawingsToAfterMap(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [mapData.drawnFeatures, mapType, syncDrawingsToAfterMap]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Set up idle rotation checker | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const checkIdle = setInterval(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -510,6 +689,18 @@ export const Mapbox: React.FC<{ position?: { latitude: number; longitude: number | |||||||||||||||||||||||||||||||||||||||||||||||||||
| stopRotation() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| setIsMapLoaded(false) // Reset map loaded state on cleanup | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| setMap(null) // Clear map instance from context | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (compareRef.current) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| compareRef.current.remove() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| compareRef.current = null | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (afterMap.current) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMap.current.remove() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMap.current = null | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMapMarkersRef.current.forEach(m => m.remove()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterMapMarkersRef.current = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| map.current.remove() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| map.current = null | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -633,14 +824,20 @@ export const Mapbox: React.FC<{ position?: { latitude: number; longitude: number | |||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="relative h-full w-full"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="relative h-full w-full overflow-hidden rounded-l-lg" id="comparison-container"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ref={mapContainer} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="h-full w-full overflow-hidden rounded-l-lg" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| className={mapType === MapToggleEnum.DrawingMode ? "absolute inset-0" : "h-full w-full"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onMouseDown={handleMouseDown} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onMouseUp={handleMouseUp} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onMouseLeave={handleMouseUp} // Clear timer if mouse leaves container while pressed | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onMouseLeave={handleMouseUp} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| {mapType === MapToggleEnum.DrawingMode && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ref={afterMapContainer} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="absolute inset-0 pointer-events-none" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+835
to
+840
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The The Compare control needs to capture pointer events on both map containers to function correctly. 🐛 Proposed fix {mapType === MapToggleEnum.DrawingMode && (
<div
ref={afterMapContainer}
- className="absolute inset-0 pointer-events-none"
+ className="absolute inset-0"
/>
)}🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,21 @@ | ||||||||||
| declare module 'mapbox-gl-compare' { | ||||||||||
| import { Map } from 'mapbox-gl'; | ||||||||||
|
|
||||||||||
| export interface CompareOptions { | ||||||||||
| orientation?: 'vertical' | 'horizontal'; | ||||||||||
| mousemove?: boolean; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| export default class Compare { | ||||||||||
| constructor( | ||||||||||
| a: Map, | ||||||||||
| b: Map, | ||||||||||
| container: string | HTMLElement, | ||||||||||
| options?: CompareOptions | ||||||||||
| ); | ||||||||||
| remove(): void; | ||||||||||
| on(type: string, fn: Function): this; | ||||||||||
| off(type: string, fn: Function): this; | ||||||||||
|
Comment on lines
+17
to
+18
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider using stricter types for event handlers. Using ♻️ Suggested improvement- on(type: string, fn: Function): this;
- off(type: string, fn: Function): this;
+ on(type: 'slideend', fn: (event: { currentTarget: Compare }) => void): this;
+ off(type: 'slideend', fn: (event: { currentTarget: Compare }) => void): this;Alternatively, if multiple event types are supported, use a union type or generic constraint. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||
| setSlider(x: number): void; | ||||||||||
| } | ||||||||||
|
Comment on lines
+1
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The type declaration for Also, the event SuggestionReplace export default class Compare {
// ...
on(type: string, fn: (...args: unknown[]) => void): this
off(type: string, fn: (...args: unknown[]) => void): this
}Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion. |
||||||||||
| } | ||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -73,6 +73,7 @@ | |
| "lottie-react": "^2.4.1", | ||
| "lucide-react": "^0.507.0", | ||
| "mapbox-gl": "^3.11.0", | ||
| "mapbox-gl-compare": "^0.4.2", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check the peer dependencies, dependencies, and metadata for mapbox-gl-compare v0.4.2
curl -s https://registry.npmjs.org/mapbox-gl-compare/0.4.2 | jq '.peerDependencies, .dependencies, .version'
echo "---"
# Check the latest version available and its details
curl -s https://registry.npmjs.org/mapbox-gl-compare/latest | jq '.peerDependencies, .dependencies, .version'
echo "---"
# Check all versions available
curl -s https://registry.npmjs.org/mapbox-gl-compare | jq '.["dist-tags"]'Repository: QueueLab/QCX Length of output: 205 Test The 🤖 Prompt for AI Agents |
||
| "next": "15.3.6", | ||
| "next-themes": "^0.3.0", | ||
| "open-codex": "^0.1.30", | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Redundant geometry extraction.
Line 74 extracts only geometries, but lines 78-82 rebuild features using the original
drawnFeaturesarray anyway. The intermediatefeaturesvariable is unnecessary.♻️ Simplified approach
const source = afterMap.current.getSource('draw-mirror') as mapboxgl.GeoJSONSource; - const features = mapData.drawnFeatures?.map(df => df.geometry) || []; source.setData({ type: 'FeatureCollection', - features: features.map((g, i) => ({ + features: (mapData.drawnFeatures || []).map(df => ({ type: 'Feature', - geometry: g, - properties: mapData.drawnFeatures?.[i] || {} + geometry: df.geometry, + properties: { id: df.id, measurement: df.measurement, type: df.type } })) });🤖 Prompt for AI Agents