frontend/src/maps/mapControllers/Geocoder.tsx

153 lines
4.7 KiB
TypeScript

import { ControlPosition, useMap } from "react-map-gl/mapbox-legacy";
import MapboxGeocoder, { Result } from "@mapbox/mapbox-gl-geocoder";
import { useEffect, useState } from "react";
// export interface GeocoderObject {
// id: string;
// place_name: string;
// center: number[];
// place_type: string[];
// }
// interface GeocoderResult {
// result: GeocoderObject;
// }
interface Props {
mapboxAccessToken: string;
position: ControlPosition;
customEntries?: Result[];
customTransition?: (objectResult: Result) => void;
resultFunction?: (objectResult: Result) => void;
}
//const MAPBOX_TOKEN = process.env.REACT_APP_MAPBOX_ACCESS_TOKEN;
// const MAPBOX_TOKEN = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN;
// const geoCoder = new MapboxGeocoder({
// accessToken: MAPBOX_TOKEN ?? "",
// flyTo: true,
// marker: false,
// zoom: 18
// });
export default function Geocoder(props: Props) {
const { mapboxAccessToken, position, customEntries, resultFunction, customTransition } = props;
const [geoCoder, setGeoCoder] = useState<MapboxGeocoder>()
const { current: map } = useMap();
const localSearch = (query: string) => {
const matchesCoords = query.match(
/^[ ]*(?:Lat: )?(-?\d+\.?\d*)[, ]+(?:Lng: )?(-?\d+\.?\d*)[ ]*$/i
);
let matchingResults: Result[] = [];
if (matchesCoords) {
const coord1 = Number(matchesCoords[1]);
const coord2 = Number(matchesCoords[2]);
const matchingCoords: Result[] = [];
if (coord1 < -90 || coord1 > 90) {
// coord1 is longitude
matchingCoords.push({
id: "Lat: " + coord2 + " Lng: " + coord1,
place_name: "Lat: " + coord2 + " Lng: " + coord1,
center: [coord1, coord2],
place_type: ["coordinate"]
} as Result);
}
if (coord2 < -90 || coord2 > 90) {
// coord2 is longitude
matchingCoords.push({
id: "Lat: " + coord1 + " Lng: " + coord2,
place_name: "Lat: " + coord1 + " Lng: " + coord2,
center: [coord2, coord1],
place_type: ["coordinate"]
} as Result);
}
if (matchingCoords.length === 0) {
// else could be either lng, lat or lat, lng
matchingCoords.push({
id: "Lat: " + coord2 + " Lng: " + coord1,
place_name: "Lat: " + coord2 + " Lng: " + coord1,
center: [coord1, coord2],
place_type: ["coordinate"]
} as Result);
matchingCoords.push({
id: "Lat: " + coord1 + " Lng: " + coord2,
place_name: "Lat: " + coord1 + " Lng: " + coord2,
center: [coord2, coord1],
place_type: ["coordinate"]
} as Result);
}
matchingResults = matchingResults.concat(matchingCoords);
}
customEntries?.forEach(obj => {
if (obj.place_name.toLowerCase().includes(query.toLowerCase())) {
matchingResults.push(obj);
}
});
return matchingResults;
};
//create the geocoder
useEffect(()=>{
const g = new MapboxGeocoder({
accessToken: mapboxAccessToken,
localGeocoder: localSearch,
flyTo: customTransition ? false : true,
marker: false,
zoom: 18,
});
setGeoCoder(g)
},[mapboxAccessToken])
// //set the geocoder options
// useEffect(() => {
// //connects the localSearch function to the geocoder if there are custom entries passed in
// if (customEntries) {
// //need to determine a new way to handle custom search with updated geocoder
// //geoCoder.options.localGeocoder = localSearch;
// }
// //disables the transition that the geocoder would fire if a custom transition was passed in
// if (customTransition) {
// geoCoder.setFlyTo(false);
// }
// }, [customEntries, customTransition]); // eslint-disable-line react-hooks/exhaustive-deps
//attach custom functions to event handlers
useEffect(() => {
if(geoCoder){
//event is fired when the search bar is cleared
geoCoder.on("clear", () => {});
//event is fired when starting to load the results
geoCoder.on("loading", () => {});
//event is fired when the results come back from a search
geoCoder.on("results", () => {});
//event is fired when there is an error
geoCoder.on("error", () => {});
//event is fired when an option is selected from the results
geoCoder.on("result", (res: Result) => {
if (resultFunction) {
resultFunction(res);
}
if (customTransition) {
customTransition(res);
}
});
//once the map ref and access token are set up add the control to the map
if (map) {
map.addControl(geoCoder, position);
}
}
}, [map, geoCoder]); // eslint-disable-line react-hooks/exhaustive-deps
return null;
}