Display pois
This commit is contained in:
parent
c157c2d5ec
commit
433fca64ad
6 changed files with 412 additions and 10 deletions
|
|
@ -8,6 +8,9 @@ import type {
|
|||
HexagonData,
|
||||
ViewChangeParams,
|
||||
ApiResponse,
|
||||
POI,
|
||||
POIResponse,
|
||||
POICategoryGroup,
|
||||
} from './types';
|
||||
|
||||
const DEBOUNCE_MS = 150;
|
||||
|
|
@ -47,6 +50,12 @@ export default function App() {
|
|||
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const abortControllerRef = useRef<AbortController | null>(null);
|
||||
|
||||
// POI state
|
||||
const [pois, setPois] = useState<POI[]>([]);
|
||||
const [selectedPOICategories, setSelectedPOICategories] = useState<Set<POICategoryGroup>>(new Set());
|
||||
const poiDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const poiAbortControllerRef = useRef<AbortController | null>(null);
|
||||
|
||||
// Debounced fetch when dependencies change
|
||||
useEffect(() => {
|
||||
if (!bounds) return;
|
||||
|
|
@ -95,6 +104,49 @@ export default function App() {
|
|||
};
|
||||
}, [filters, resolution, bounds]);
|
||||
|
||||
// Fetch POIs when bounds or selected categories change
|
||||
useEffect(() => {
|
||||
if (!bounds || selectedPOICategories.size === 0) {
|
||||
setPois([]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (poiDebounceRef.current) {
|
||||
clearTimeout(poiDebounceRef.current);
|
||||
}
|
||||
|
||||
poiDebounceRef.current = setTimeout(async () => {
|
||||
if (poiAbortControllerRef.current) {
|
||||
poiAbortControllerRef.current.abort();
|
||||
}
|
||||
poiAbortControllerRef.current = new AbortController();
|
||||
|
||||
try {
|
||||
const boundsStr = `${bounds.south},${bounds.west},${bounds.north},${bounds.east}`;
|
||||
const categoriesStr = Array.from(selectedPOICategories).join(',');
|
||||
const params = new URLSearchParams({
|
||||
categories: categoriesStr,
|
||||
bounds: boundsStr,
|
||||
});
|
||||
const res = await fetch(`${getApiBaseUrl()}/api/pois?${params}`, {
|
||||
signal: poiAbortControllerRef.current.signal,
|
||||
});
|
||||
const json: POIResponse = await res.json();
|
||||
setPois(json.features || []);
|
||||
} catch (err) {
|
||||
if (err instanceof Error && err.name !== 'AbortError') {
|
||||
console.error('Failed to fetch POIs:', err);
|
||||
}
|
||||
}
|
||||
}, DEBOUNCE_MS);
|
||||
|
||||
return () => {
|
||||
if (poiDebounceRef.current) {
|
||||
clearTimeout(poiDebounceRef.current);
|
||||
}
|
||||
};
|
||||
}, [bounds, selectedPOICategories]);
|
||||
|
||||
const handleViewChange = useCallback(
|
||||
({ resolution: newRes, bounds: newBounds, zoom: newZoom }: ViewChangeParams) => {
|
||||
setResolution(newRes);
|
||||
|
|
@ -106,9 +158,15 @@ export default function App() {
|
|||
|
||||
return (
|
||||
<div className="h-screen flex">
|
||||
<Filters filters={filters} onChange={setFilters} zoom={zoom} />
|
||||
<Filters
|
||||
filters={filters}
|
||||
onChange={setFilters}
|
||||
zoom={zoom}
|
||||
selectedPOICategories={selectedPOICategories}
|
||||
onPOICategoriesChange={setSelectedPOICategories}
|
||||
/>
|
||||
<div className="flex-1 relative">
|
||||
<Map data={data} onViewChange={handleViewChange} />
|
||||
<Map data={data} pois={pois} onViewChange={handleViewChange} />
|
||||
{loading && (
|
||||
<div className="absolute top-4 right-4 bg-white px-3 py-1 rounded shadow">Loading...</div>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue