feat: implement map fly-to functionality with custom events for improved navigation experience
This commit is contained in:
parent
a2f51e6837
commit
6327d7b886
|
@ -256,19 +256,40 @@ export default function SearchTooltip({ onControlChange, activeControl, crimes =
|
||||||
const handleFlyToIncident = () => {
|
const handleFlyToIncident = () => {
|
||||||
if (!selectedSuggestion || !selectedSuggestion.locations.latitude || !selectedSuggestion.locations.longitude) return;
|
if (!selectedSuggestion || !selectedSuggestion.locations.latitude || !selectedSuggestion.locations.longitude) return;
|
||||||
|
|
||||||
const flyToEvent = new CustomEvent('incident_click', {
|
// First, trigger a separate mapbox_fly_to event to handle the camera movement
|
||||||
|
const flyToMapEvent = new CustomEvent('mapbox_fly_to', {
|
||||||
detail: {
|
detail: {
|
||||||
longitude: selectedSuggestion.locations.longitude,
|
longitude: selectedSuggestion.locations.longitude,
|
||||||
latitude: selectedSuggestion.locations.latitude,
|
latitude: selectedSuggestion.locations.latitude,
|
||||||
id: selectedSuggestion.id,
|
|
||||||
zoom: 15,
|
zoom: 15,
|
||||||
description: selectedSuggestion.description,
|
bearing: 0,
|
||||||
status: selectedSuggestion.status
|
pitch: 45,
|
||||||
|
duration: 2000,
|
||||||
},
|
},
|
||||||
bubbles: true
|
bubbles: true
|
||||||
});
|
});
|
||||||
|
|
||||||
document.dispatchEvent(flyToEvent);
|
document.dispatchEvent(flyToMapEvent);
|
||||||
|
|
||||||
|
// Wait for the fly animation to complete before showing the popup
|
||||||
|
setTimeout(() => {
|
||||||
|
// Then trigger the incident_click event to show the popup
|
||||||
|
const incidentEvent = new CustomEvent('incident_click', {
|
||||||
|
detail: {
|
||||||
|
id: selectedSuggestion.id,
|
||||||
|
longitude: selectedSuggestion.locations.longitude,
|
||||||
|
latitude: selectedSuggestion.locations.latitude,
|
||||||
|
description: selectedSuggestion.description,
|
||||||
|
status: selectedSuggestion.status,
|
||||||
|
timestamp: selectedSuggestion.timestamp,
|
||||||
|
crime_categories: selectedSuggestion.crime_categories
|
||||||
|
},
|
||||||
|
bubbles: true
|
||||||
|
});
|
||||||
|
|
||||||
|
document.dispatchEvent(incidentEvent);
|
||||||
|
}, 2100); // Slightly longer than the fly animation duration
|
||||||
|
|
||||||
setShowInfoBox(false);
|
setShowInfoBox(false);
|
||||||
setSelectedSuggestion(null);
|
setSelectedSuggestion(null);
|
||||||
toggleSearch();
|
toggleSearch();
|
||||||
|
|
|
@ -185,6 +185,46 @@ export default function CrimeMap() {
|
||||||
};
|
};
|
||||||
}, [filteredCrimes]);
|
}, [filteredCrimes]);
|
||||||
|
|
||||||
|
// Set up event listener for fly-to map control
|
||||||
|
useEffect(() => {
|
||||||
|
const handleMapFlyTo = (e: CustomEvent) => {
|
||||||
|
if (!e.detail) {
|
||||||
|
console.error("Invalid fly-to data:", e.detail);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { longitude, latitude, zoom, bearing, pitch, duration } = e.detail;
|
||||||
|
|
||||||
|
// Find the map instance
|
||||||
|
const mapInstance = mapContainerRef.current?.querySelector('.mapboxgl-map');
|
||||||
|
if (!mapInstance) {
|
||||||
|
console.error("Map instance not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and dispatch a custom event that MapView component will listen for
|
||||||
|
const mapboxEvent = new CustomEvent('mapbox_fly', {
|
||||||
|
detail: {
|
||||||
|
center: [longitude, latitude],
|
||||||
|
zoom: zoom || 15,
|
||||||
|
bearing: bearing || 0,
|
||||||
|
pitch: pitch || 45,
|
||||||
|
duration: duration || 2000
|
||||||
|
},
|
||||||
|
bubbles: true
|
||||||
|
});
|
||||||
|
|
||||||
|
mapInstance.dispatchEvent(mapboxEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add event listener
|
||||||
|
document.addEventListener('mapbox_fly_to', handleMapFlyTo as EventListener);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mapbox_fly_to', handleMapFlyTo as EventListener);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Handle year-month timeline change
|
// Handle year-month timeline change
|
||||||
const handleTimelineChange = useCallback((year: number, month: number, progress: number) => {
|
const handleTimelineChange = useCallback((year: number, month: number, progress: number) => {
|
||||||
setSelectedYear(year)
|
setSelectedYear(year)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import type React from "react"
|
import type React from "react"
|
||||||
import { useState, useCallback, useRef } from "react"
|
import { useState, useCallback, useRef, useEffect } from "react"
|
||||||
import { type ViewState, Map, type MapRef, NavigationControl, GeolocateControl } from "react-map-gl/mapbox"
|
import { type ViewState, Map, type MapRef, NavigationControl, GeolocateControl } from "react-map-gl/mapbox"
|
||||||
import { FullscreenControl } from "react-map-gl/mapbox"
|
import { FullscreenControl } from "react-map-gl/mapbox"
|
||||||
import { BASE_BEARING, BASE_LATITUDE, BASE_LONGITUDE, BASE_PITCH, BASE_ZOOM, MAPBOX_STYLES, type MapboxStyle } from "@/app/_utils/const/map"
|
import { BASE_BEARING, BASE_LATITUDE, BASE_LONGITUDE, BASE_PITCH, BASE_ZOOM, MAPBOX_STYLES, type MapboxStyle } from "@/app/_utils/const/map"
|
||||||
|
@ -79,6 +79,34 @@ export default function MapView({
|
||||||
[onMoveEnd],
|
[onMoveEnd],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!mapRef.current) return;
|
||||||
|
|
||||||
|
const mapElement = mapRef.current.getMap().getContainer();
|
||||||
|
|
||||||
|
// Handle fly to event
|
||||||
|
const handleMapFly = (e: CustomEvent) => {
|
||||||
|
if (!e.detail || !mapRef.current) return;
|
||||||
|
|
||||||
|
const { center, zoom, bearing, pitch, duration } = e.detail;
|
||||||
|
|
||||||
|
mapRef.current.flyTo({
|
||||||
|
center,
|
||||||
|
zoom,
|
||||||
|
bearing,
|
||||||
|
pitch,
|
||||||
|
duration,
|
||||||
|
essential: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
mapElement.addEventListener('mapbox_fly', handleMapFly as EventListener);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
mapElement.removeEventListener('mapbox_fly', handleMapFly as EventListener);
|
||||||
|
};
|
||||||
|
}, [mapRef.current]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`relative ${className}`}>
|
<div className={`relative ${className}`}>
|
||||||
<div className="flex h-full">
|
<div className="flex h-full">
|
||||||
|
|
Loading…
Reference in New Issue