Google 提供对其 Maps API 的强大访问,可用于各种基于位置的功能和地图相关任务。在本文中,我将解释我们如何利用 Google AutocompleteService API 构建智能地址搜索字段来替换完整地址表单。
通过使用这种方法,我们简化了地址输入过程,使其更快、更准确,从而减少了用户输入错误并改善了用户体验(1 次智能输入 vs 3 次,呵呵)。
首先,要使用 Maps API,您需要在 Google Maps Platform 中启用它并获取 API 密钥。
在我们的项目中,我们使用 @react-google-maps/api 包(npm i @react-google-maps/api)。
让我们使用自定义 useGoogleMapsApi 挂钩初始化 SDK,以使其可在应用程序中重用:
const useGoogleMapsApi = () => { const api = useJsApiLoader({ id: "app", googleMapsApiKey: "", // set your Public key }); return { ...api, }; };
地点自动完成服务提供了一个 API 来在地点内进行搜索。它提供了2种方法:
为了实现我们的搜索,我们使用了 getPlacePredictions() 方法。让我们将此方法添加到我们的代码中并从钩子中返回它。
// 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 []; } };
AutocompletionRequest 接口使用输入字段作为搜索查询和其他选项,这使得搜索更加准确。我们稍后再讨论它们。
我们将在演示中使用简单的输入和结果列表。
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> ); }
根据预测,我们对 3 个领域感兴趣:
AutocompleteService 接口有大量输入,可以使搜索更加准确。
Field componentRestrictions 允许我们将搜索结果缩小到一个特定的国家/地区。它最多支持 5 个国家/地区,并要求国家/地区代码符合 ISO 3166-1 Alpha-2 标准。您可以在这里找到国家/地区代码列表。
const { predictions } = await autocomplete.getPlacePredictions({ ...options, input: query, componentRestrictions: { country: ["gb"], }, });
为了使我们的字段不会让用户感到困惑,我们需要从搜索结果中排除公园、机场等地方。自动完成服务有一个字段类型来指定要返回的预测类型。 但 AutocompleteService 只允许一种类型。
const { predictions } = await autocomplete.getPlacePredictions({ ...options, input: query, componentRestrictions: { country: ["gb"], }, types: ["geocode"], });
但它也包括更大的行政实体,如城市或街道。我们需要用户选择准确的地址。
明白了!这就是我们所需要的,对吧?...基本上,是的。但是,它不允许用户通过邮政编码进行搜索,这对我们来说是常见的情况。
因此,为了通过街道名称和邮政编码获得精确的搜索结果,我们编写了一个自定义过滤器。感谢 StackOverflow 的灵感。
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 });
如果结果包含街道地址或邮政编码,我们假设它是正确的结果。
如果它具有地理编码类型,我们会检查前提或子前提(简而言之,建筑物编号或名称)。有关类型的更多信息,您可以在此处阅读。
我们取得的成果:
AutocompleteService 仅返回搜索预测,而不返回我们需要的地点详细信息。但是,通过地点 ID 和地理编码器,我们可以获得诸如确切地址、国家/地区、邮政编码和坐标等详细信息。
Geocoder 最初是为了在地址和坐标之间进行转换而创建的,但它完全满足了我们的需求。
如果您需要有关该地点的其他信息(例如评论和评论),可以使用 Places API。
让我们向钩子添加一个新方法:
// 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; } };
为了统一存在不同行政级别和实体的县之间的结果,Google 使用地址组件结构。让我们看一个例子:
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??.
以上是使用 Google Maps API 和 React 创建智能地址搜索的详细内容。更多信息请关注PHP中文网其他相关文章!