Google fournit un accès robuste à son API Maps, qui peut être exploitée pour diverses fonctionnalités basées sur la localisation et tâches liées aux cartes. Dans cet article, j'expliquerai comment nous avons utilisé l'API Google AutocompleteService pour créer un champ de recherche d'adresse intelligente qui remplace un formulaire d'adresse complète.
En utilisant cette approche, nous avons réduit les erreurs de saisie des utilisateurs et amélioré l'expérience utilisateur en simplifiant le processus de saisie d'adresse, le rendant plus rapide et plus précis (1 saisie intelligente contre 3, hein).
Tout d'abord, pour travailler avec l'API Maps, vous devrez l'activer dans Google Maps Platform et obtenir la clé API.
Dans notre projet, nous utilisons le package @react-google-maps/api (npm i @react-google-maps/api).
Initialisons le SDK avec le hook useGoogleMapsApi personnalisé, pour le rendre réutilisable au sein de l'application :
const useGoogleMapsApi = () => { const api = useJsApiLoader({ id: "app", googleMapsApiKey: "", // set your Public key }); return { ...api, }; };
Le service Places Autocomplete fournit une API pour effectuer une recherche dans les lieux. Il propose 2 méthodes :
Pour implémenter notre recherche, nous avons utilisé la méthode getPlacePredictions(). Ajoutons cette méthode à notre code et renvoyons-la depuis le hook.
// Function to search for places using Google Autocomplete Service const searchPlaces = async ( query: string, options?: Omit<google.maps.places.AutocompletionRequest, "input"> ): Promise<Array<google.maps.places.AutocompletePrediction>> => { // Load AutocompleteService from Google Maps API const { AutocompleteService } = (await google.maps.importLibrary( "places" )) as google.maps.PlacesLibrary; const autocomplete = new AutocompleteService(); try { // Fetch place predictions based on user query const { predictions } = await autocomplete.getPlacePredictions({ ...options, // Optional additional parameters for more precise searches input: query, // User's search query (e.g., "Baker Street") }); // Return the list of predictions to display to the user return predictions; } catch { // If there's an error, return an empty array return []; } };
L'interface AutocompletionRequest utilise le champ de saisie comme requête de recherche et d'autres options, ce qui permet de rendre une recherche plus précise. Nous en reparlerons plus tard.
Nous utiliserons une simple saisie et une liste de résultats pour notre démo.
import useGoogleMapsApi from "./useGoogleMapsApi"; import { useEffect, useState } from "react"; export default function App() { const { searchPlaces } = useGoogleMapsApi(); // State to store input value from the user const [inputValue, setInputValue] = useState<string>(""); // State to store the list of place predictions from Google Autocomplete const [places, setPlaces] = useState< Array<google.maps.places.AutocompletePrediction> >([]); // Function to handle the search process when the user types a query const handleSearch = async (searchQuery: string) => { const result = await searchPlaces(searchQuery); setPlaces(result); }; // Trigger the search whenever the input value changes useEffect(() => { handleSearch(inputValue); }, [inputValue]); return ( <div style={{ maxWidth: "80%", width: "100%",fontFamily: "sans-serif",}}> <input value={inputValue} onChange={(e) => setInputValue(e.target.value)} placeholder="Find an address" /> {places.map((place) => ( <div style={{ marginBottom: "0.5rem",}}> <span style={{ color: "blue", cursor: "pointer",}}> {place.description} </span> <span style={{ color: "#333", fontSize: "0.75rem",}}>{`(${place.place_id})`}</span> <span> - {place.types.join(", ")}</span> </div> ))} </div> ); }
D'après les prédictions, nous nous intéressons aux 3 domaines :
L'interface AutocompleteService comporte de nombreuses entrées qui permettent de rendre une recherche plus précise.
Le composant de champRestrictions nous permet de restreindre les résultats de recherche à un pays spécifique. Il prend en charge jusqu'à 5 pays et nécessite que les codes de pays soient conformes à la norme ISO 3166-1 Alpha-2. Ici vous pouvez trouver une liste des codes des pays.
const { predictions } = await autocomplete.getPlacePredictions({ ...options, input: query, componentRestrictions: { country: ["gb"], }, });
Pour que notre champ ne prête pas à confusion pour les utilisateurs, nous devons exclure des endroits comme les parcs, les aéroports, etc. des résultats de recherche. Le service Autocomplete dispose d'un champ types pour spécifier les types de prédictions à renvoyer. Mais un seul type est autorisé pour l'AutocompleteService.
const { predictions } = await autocomplete.getPlacePredictions({ ...options, input: query, componentRestrictions: { country: ["gb"], }, types: ["geocode"], });
Mais cela inclut également les plus grandes entités administratives comme les villes ou les rues. Nous avons besoin que les utilisateurs sélectionnent des adresses précises.
Je t'ai eu ! C'est ce dont nous avons besoin, n'est-ce pas ?... Au fond, oui. Cependant, il ne permet pas aux utilisateurs d'effectuer une recherche par code postal, ce qui est un cas courant chez nous.
Ainsi, pour obtenir des résultats de recherche précis par nom de rue et par code postal, nous avons écrit un filtre personnalisé. Merci à StackOverflow pour l'inspiration.
export const filterPredictions = ( results: Array<google.maps.places.AutocompletePrediction> ): Array<google.maps.places.AutocompletePrediction> => results.filter(({ types }) => { // Keep predictions that are street addresses or postal codes if (types.includes("street_address") || types.includes("postal_code")) { return true; } // For geocode types, check for building numbers (premise/subpremise) if (types.includes("geocode")) { return types.some((type) => ["premise", "subpremise"].includes(type)); } return false; // Filter out irrelevant types });
Si le résultat inclut l'adresse_rue ou le code_postal, nous le considérons comme le résultat correct.
S'il est de type géocode, nous vérifions le local ou le sous-local (en bref, le numéro ou le nom du bâtiment). Pour en savoir plus sur les types, vous pouvez lire ici.
Le résultat que nous avons obtenu :
AutocompleteService renvoie uniquement les prédictions de recherche, mais pas les détails du lieu dont nous avons besoin. Cependant, avec l'identifiant du lieu et le géocodeur, nous pouvons obtenir des détails tels que l'adresse exacte, le pays, le code postal et les coordonnées.
Geocoder a été initialement créé pour faire des conversions entre adresses et coordonnées, mais il couvre complètement nos besoins.
Si vous avez besoin d'informations supplémentaires sur le lieu, comme des avis et des commentaires, vous pouvez utiliser l'API Places.
Ajoutons une nouvelle méthode à notre hook :
// Function to get detailed information about a place using its place id const getPlaceById = async ( placeId: string // The place_id from the AutocompleteService ): Promise<google.maps.GeocoderResult | null> => { const geocoder = new google.maps.Geocoder(); // Create a new instance of Geocoder try { const { results } = await geocoder.geocode({ placeId }); // Return the first result return results[0]; } catch { // In case of error, return null return null; } };
Pour unifier les résultats entre les comtés, où existent différents niveaux administratifs et entités, Google utilise la structure des composants d'adresse. Vérifions un exemple :
To format the address, we need the Baker Street 221B, NW1 6XE, London (street_number route, postal_code, locality). But, in some cases, the keys structure might differ. To cover it we made an unified serializer:
// Helper function to extract a specific address component by its type (e.g., street_address, postal_code) export const pickAddressComponentByType = ( result: google.maps.GeocoderResult, type: Array<string> ): string => result.address_components.find((component) => component.types.some((componentType) => type.includes(componentType)) )?.long_name || ""; // Function to serialize the geocoded result into a structured format export const serializeGeocoderResult = ( result: google.maps.GeocoderResult ): TFormatedGeocoderResult => ({ formattedAddress: result.formatted_address, streetAddress: pickAddressComponentByType(result, [ "street_address", "premise", "route", ]), streetNumber: pickAddressComponentByType(result, ["street_number"]), city: pickAddressComponentByType(result, [ "locality", "postal_town", "administrative_area_level_2", ]), country: pickAddressComponentByType(result, ["country"]), state: pickAddressComponentByType(result, ["administrative_area_level_1"]), postalCode: pickAddressComponentByType(result, ["postal_code"]), latitude: result.geometry.location.lat(), longitude: result.geometry.location.lng(), });
Notice, that we verified it only for Great Britain. So maybe you might need to enhance it for your specific cases.
In this article, I have shown how we can use different Google Maps API's to build address fields, that can replace full address forms and drastically reduce the time users need to spend on it, reducing the number of mistakes on the users' side.
Check out the DEMO sandbox, but don’t forget to set your own GOOGLE_MAPS_API_KEY.
Feel free to send your questions and suggestions in comments. I'll be glad for any conversations??.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!