<html lang="en"> <head> <meta charset="utf-8"> <title>Location Selection Demo</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> </head> <body> <h1>Location Selection Demo - FindPickupPointsForPlace</h1> <div class="container"> <section class="form-container"> <form id="form-pups-for-place" name="location-selection"> <label class="form-label" for="placeId">Place ID</label> <input type="text" id="placeId" name="placeId" value="ChIJwTUa-q_Mj4ARff4yludGH-M" /> <label class="form-label" for="languageCode">Language Code</label> <input type="text" id="languageCode" name="languageCode" value="en-US" /> <label class="form-label" for="regionCode">Region Code</label> <input type="text" id="regionCode" name="regionCode" value="US" /> <label class="form-label" for="searchLocation-latitude">Search Location - Latitude</label> <input type="text" id="searchLocation-latitude" name="searchLocation-latitude" value="37.329472" /> <label class="form-label" for="searchLocation-longitude">Search Location - Longitude</label> <input type="text" id="searchLocation-longitude" name="searchLocation-longitude" value="-121.890449" /> <label class="form-label" for="orderBy">Order By</label> <select id="orderBy" name="orderBy"> <option value="DISTANCE_FROM_SEARCH_LOCATION" selected>DISTANCE_FROM_SEARCH_LOCATION</option> <option value="WALKING_ETA_FROM_SEARCH_LOCATION">WALKING_ETA_FROM_SEARCH_LOCATION</option> <option value="DRIVING_ETA_FROM_PICKUP_POINT_TO_DESTINATION">DRIVING_ETA_FROM_PICKUP_POINT_TO_DESTINATION</option> </select> <label class="form-label" for="destination-latitude">Destination - Latitude</label> <input type="text" id="destination-latitude" name="destination-latitude" value="" /> <label class="form-label" for="destination-longitude">Destination - Longitude</label> <input type="text" id="destination-longitude" name="destination-longitude" value="" /> <label class="form-label" for="maxResults">Max Results</label> <input type="number" id="maxResults" name="maxResults" min="1" value="5" step="1" /> <fieldset> <legend>Travel Modes</legend> <div> <input type="checkbox" id="walking" name="travelModes" value="WALKING" checked> <label for="walking" class="form-checkbox-label">WALKING</label> </div> <div> <input type="checkbox" id="driving" name="travelModes" value="DRIVING" checked> <label for="driving" class="form-checkbox-label">DRIVING</label> </div> <div> <input type="checkbox" id="twoWheeler" name="travelModes" value="TWO_WHEELER"> <label for="twoWheeler" class="form-checkbox-label">TWO_WHEELER</label> </div> </fieldset> <label class="form-label" for="computeWalkingEta">Compute Walking ETA</label> <select id="computeWalkingEta" name="computeWalkingEta" class="boolean"> <option value="true">true</option> <option value="false" selected>false</option> </select> <label class="form-label" for="computeDrivingEta">Compute Driving ETA</label> <select id="computeDrivingEta" name="computeDrivingEta" class="boolean"> <option value="true">true</option> <option value="false" selected>false</option> </select> <input class="submit-button" type="submit" value="Call" /> </form> </section> <section> <div id="map" class="map"></div> </section> </div> <section class="output-container"> <h2>Response</h2> <pre id="output"></pre> </section> </body> </html>
body { font-family: 'Google Sans'; } .container { display: grid; grid-template-columns: 30% 1fr; grid-template-rows: 100%; grid-column-gap: 20px; grid-row-gap: 0px; } h1 { font-size: 24px; margin-top: 20px; margin-bottom: 20px; font-weight: bold; } h2 { font-size: 18px; font-weight: bold; } h1, .form-container, .output-container { margin-left: 20px; } .map, .output-container { margin-right: 20px; } .form-container { border: 1px solid black; padding: 20px; } .map { border: 1px solid black; min-height: 800px; } .output-container { margin-top: 20px; } #output { border: 1px solid red; font-family: 'Google Sans'; min-height: 150px; } label:not(.form-checkbox-label), legend { overflow-wrap: break-word; font-weight: bold; } input:not([type="checkbox"]), select, fieldset { font-family: 'Google Sans'; width: 100%; padding: 5px 5px; margin: 0 0 20px 0; display: inline-block; border: 1px solid #ccc; border-radius: 8px; box-sizing: border-box; } input[type="submit"] { min-width: 150px; background-color: green; /* Blue */ border: none; color: white; padding: 15px 15px; text-decoration: none; display: inline-block; font-size: 16px; border-radius: 20px; width: 50%; } input[type="submit"]:hover { background-color: darkseagreen; } input[type="submit"]:active { background-color: darkseagreen; box-shadow: 0 5px #666; transform: translateY(4px); } .info-label { font-weight: bold; }
const MAPS_API_KEY = ''; // Put your API Key for Maps SDK here const LS_API_KEY = ''; // Put your API Key for Location Selection APIs here const MAPS_URL = `https://maps.googleapis.com/maps/api/js?key=${ MAPS_API_KEY}&libraries=places,geometry&callback=initMap`; const LS_BASE_URL = 'https://locationselection.googleapis.com/v1beta'; const API_URL_PUPS_FOR_PLACE = `${LS_BASE_URL}:findPickupPointsForPlace?key=${LS_API_KEY}`; const API_URL_PUPS_FOR_LOCATION = `${LS_BASE_URL}:findPickupPointsForLocation?key=${LS_API_KEY}`; const API_URL_NEARBY_PLACES = `${LS_BASE_URL}:findNearbyPlaces?key=${LS_API_KEY}`; const FORM_ID_PUPS_FOR_LOCATION = 'form-pups-for-location'; const FORM_ID_PUPS_FOR_PLACE = 'form-pups-for-place'; const FORM_ID_NEARBY_PLACES = 'form-nearby-places'; const FORM_TO_API_URL_MAP = { [FORM_ID_PUPS_FOR_LOCATION]: API_URL_PUPS_FOR_LOCATION, [FORM_ID_PUPS_FOR_PLACE]: API_URL_PUPS_FOR_PLACE, [FORM_ID_NEARBY_PLACES]: API_URL_NEARBY_PLACES, }; const RED_PIN = 'http://maps.google.com/mapfiles/ms/icons/red-dot.png'; const GREEN_PIN = 'http://maps.google.com/mapfiles/ms/icons/green-dot.png'; const BLUE_PIN = 'http://maps.google.com/mapfiles/ms/icons/blue-dot.png'; const DEFAULT_ZOOM_LEVEL = 18; // codepoint from https://fonts.google.com/icons const SEARCH_LOCATION_MARKER = '\ue7f2'; const GOOGLEPLEX = { lat: 37.422001, lng: -122.084061 }; let map; let polyLines = []; let polygons = []; let mapMarkers = []; let entranceMarkers = []; function loadMap() { const script = document.createElement('script'); script.src = MAPS_URL; document.body.appendChild(script); } function initMap() { map = new google.maps.Map( document.getElementById('map'), {center: GOOGLEPLEX, zoom: DEFAULT_ZOOM_LEVEL}); } function setupForm() { const form = document.getElementsByTagName('form')[0]; form.addEventListener('submit', onFormSubmit); } function onFormSubmit(evt) { evt.preventDefault(); evt.stopPropagation(); const formData = new FormData(evt.target); fetchAPIResults(formData); } function transformFormData(fd) { let transformedFd = { localizationPreferences: {}, }; const formId = document.getElementsByTagName('form')[0].id; if (formId === FORM_ID_PUPS_FOR_LOCATION || formId === FORM_ID_PUPS_FOR_PLACE) { transformedFd = {localizationPreferences: {}, travelModes: []}; } const addSearchLocation = () => { if (transformedFd.searchLocation == null) { transformedFd.searchLocation = {}; } }; const addDestination = () => { if (transformedFd.destination == null) { transformedFd.destination = {}; } }; fd.forEach((value, key) => { switch (key) { case 'travelModes': transformedFd.travelModes.push(value); break; case 'languageCode': transformedFd.localizationPreferences[key] = value; break; case 'regionCode': transformedFd.localizationPreferences[key] = value; break; case 'searchLocation-latitude': if (value) { addSearchLocation(); transformedFd.searchLocation['latitude'] = value; } break; case 'searchLocation-longitude': if (value) { addSearchLocation(); transformedFd.searchLocation['longitude'] = value; } break; case 'destination-latitude': if (value) { addDestination(); transformedFd.destination['latitude'] = value; } break; case 'destination-longitude': if (value) { addDestination(); transformedFd.destination['longitude'] = value; } break; default: transformedFd[key] = value; break; } }); const json = JSON.stringify(transformedFd, undefined, 2); return json; } async function fetchAPIResults(fd) { const formId = document.getElementsByTagName('form')[0].id; const url = FORM_TO_API_URL_MAP[formId]; const transformedFd = transformFormData(fd); const response = await fetch(url, {method: 'POST', body: transformedFd}); const result = await response.json(); // Display JSON displayAPIResults(result); // Update map let searchLocation = {}; if (JSON.parse(transformedFd).searchLocation) { searchLocation = { lat: Number(JSON.parse(transformedFd).searchLocation.latitude), lng: Number(JSON.parse(transformedFd).searchLocation.longitude), }; } switch (formId) { case FORM_ID_PUPS_FOR_PLACE: markPickupPointsForPlace(result); break; case FORM_ID_PUPS_FOR_LOCATION: markPickupPointsForLocation(result, searchLocation); break; case FORM_ID_NEARBY_PLACES: markNearbyPlaces(result, searchLocation); break; default: break; } } function displayAPIResults(data) { const output = document.getElementById('output'); output.textContent = JSON.stringify(data, undefined, 2); } function markNearbyPlaces(data, searchLocation) { if (data.error) { resetMap(); return; } const places = []; for (const placeResult of data.placeResults) { places.push(placeResult.place); } resetMap(searchLocation); markPlaces(places, searchLocation); for (const place of places) { markEntrances(place.associatedCompounds, place); } markSearchLocation(searchLocation, ''); for (const place of places) { mapPolygons(place.associatedCompounds); } } function markPickupPointsForPlace(data) { if (data.error) { resetMap(); return; } const place = data.placeResult.place; const pickupPoints = data.pickupPointResults; const searchLocation = { lat: place.geometry.location.latitude, lng: place.geometry.location.longitude }; resetMap(searchLocation); markPickupPoints(place, pickupPoints, searchLocation); markEntrances(place.associatedCompounds, place); markSearchLocation(searchLocation, place.displayName); createPolyLinesOneToMany(searchLocation, pickupPoints); mapPolygons(place.associatedCompounds); } function markPickupPointsForLocation(data, searchLocation) { if (data.error) { resetMap(); return; } const placeIdToPlace = {}; // A dict, and the key is placeId(str)s and the value is a list of pups. const placePickupPoints = {}; data.placeResults.forEach(result => { placeIdToPlace[result.place.placeId] = result.place; placePickupPoints[result.place.placeId] = []; }); data.placePickupPointResults.forEach(result => { placePickupPoints[result.associatedPlaceId].push(result.pickupPointResult); }) resetMap(searchLocation); for (const placeId in placePickupPoints) { const place = placeIdToPlace[placeId]; const pups = placePickupPoints[placeId]; markEntrances(place.associatedCompounds, place); markPickupPoints(place, pups, searchLocation); createPolyLinesOneToMany(searchLocation, pups); mapPolygons(place.associatedCompounds); } // update the marker rank to global order for (let i = 0; i < mapMarkers.length; i++) { mapMarkers[i].label = String(i); } markSearchLocation(searchLocation, ''); } function markPlaces(places, searchLocation) { for (const place of places) { const placeLocation = place.geometry.location; const infoWindow = new google.maps.InfoWindow({content: createInfoWindow(place, null)}); const marker = new google.maps.Marker({ position: toLatLngLiteral(placeLocation), animation: google.maps.Animation.DROP, map: map, }); marker.addListener('click', () => { infoWindow.open(map, marker); }); map.addListener('click', () => { infoWindow.close(); }); mapMarkers.push(marker); } } function markEntrances(compounds, place) { if (!compounds) { return; } for (const compound of compounds) { if (!compound.entrances) { continue; } for (const entrance of compound.entrances) { const entranceMarker = new google.maps.Marker({ position: toLatLngLiteral(entrance.location), icon: { url: BLUE_PIN, }, animation: google.maps.Animation.DROP, map: map, }); const infoWindow = new google.maps.InfoWindow({content: createInfoWindow(place, null)}); entranceMarker.addListener('click', () => { infoWindow.open(map, entranceMarker); }); map.addListener('click', () => { infoWindow.close(); }); entranceMarkers.push(entranceMarker); } } } function mapPolygons(many) { if (!many) { return; } for (const toPoint of many) { const data = toPoint.geometry.displayBoundary; if (data == null || data.coordinates == null) { continue; } const value = data.coordinates; const polyArray = JSON.parse(JSON.stringify(value))[0]; const usedColors = []; const finalLatLngs = []; let color = ''; for (let i = 0; i < polyArray.length; ++i) { if (polyArray[i] != null && polyArray[i].length > 0) { color = getColor(usedColors); usedColors.push(color); if (isArrLatLng(polyArray[i])) { finalLatLngs.push({lat: polyArray[i][1], lng: polyArray[i][0]}); } } } const poly = new google.maps.Polygon({ strokeColor: color, strokeOpacity: 0.2, strokeWeight: 5, fillColor: color, fillOpacity: 0.1, paths: finalLatLngs, map: map, }); polygons.push(poly); } } function getColor(usedColors) { let color = generateStrokeColor(); while (usedColors.includes(color)) { color = generateStrokeColor(); } return color; } function generateStrokeColor() { return Math.floor(Math.random() * 16777215).toString(16); } function isArrLatLng(currArr) { if (!currArr || currArr.length !== 2) { return false; } return ((typeof currArr[0]) === 'number') && ((typeof currArr[1]) === 'number'); } function toLatLngLiteral(latlng) { return {lat: latlng.latitude, lng: latlng.longitude}; } function pickupHasRestrictions(pickupPointData) { let hasRestrictions = false; const travelDetails = pickupPointData.travelDetails; for (let i = 0; i < travelDetails.length; i++) { if (travelDetails[i].trafficRestriction !== 'NO_RESTRICTION') { hasRestrictions = true; } } return hasRestrictions; } function markPickupPoints(place, pickupPoints, searchLocation) { for (let i = 0; i < pickupPoints.length; i++) { const pickupPointData = pickupPoints[i]; const pickupPoint = pickupPoints[i].pickupPoint; const pupIcon = pickupHasRestrictions(pickupPointData) ? RED_PIN : GREEN_PIN; const contentString = createInfoWindow(place, pickupPoint); const pupInfoWindow = new google.maps.InfoWindow({content: contentString}); const marker = new google.maps.Marker({ position: toLatLngLiteral(pickupPoint.location), label: { text: String(i), fontWeight: 'bold', fontSize: '20px', color: '#000' }, animation: google.maps.Animation.DROP, map, icon: { url: pupIcon, anchor: new google.maps.Point(14, 43), labelOrigin: new google.maps.Point(-5, 5) }, }); marker.addListener('click', () => { pupInfoWindow.open(map, marker); }); map.addListener('click', () => { pupInfoWindow.close(); }); mapMarkers.push(marker); } } function createInfoWindow(place, pickupPoint) { let result = []; const addResult = (value, key, map) => result.push(`<p><span class="info-label">${key}:</span> ${value}</p>`); const formatAddress = (address) => address.lines.join(','); const placeFieldMap = new Map(); if (place !== null) { placeFieldMap.set('Place', ''); placeFieldMap.set('Name', place.displayName); placeFieldMap.set('Place ID', place.placeId); placeFieldMap.set('Address', formatAddress(place.address.formattedAddress)); } const pickupPointFieldMap = new Map(); if (pickupPoint !== null) { pickupPointFieldMap.set('Pickup point', ''); pickupPointFieldMap.set('Name', pickupPoint.displayName); } placeFieldMap.forEach(addResult); result.push('<hr/>'); pickupPointFieldMap.forEach(addResult); return result.join(''); } function markSearchLocation(location, label) { const infoWindow = new google.maps.InfoWindow({content: `<p><b>Name: </b>${label}</p>`}); const marker = new google.maps.Marker({ position: location, map, label: { text: SEARCH_LOCATION_MARKER, fontFamily: 'Material Icons', color: '#ffffff', fontSize: '18px', fontWeight: 'bold', }, }); marker.addListener('click', () => { infoWindow.open(map, marker); }); map.addListener('click', () => { infoWindow.close(); }); mapMarkers.push(marker); } function createPolyLinesOneToMany(one, many) { const lineSymbol = { path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW, }; for (const toPoint of many) { const line = new google.maps.Polyline({ path: [one, toLatLngLiteral(toPoint.pickupPoint.location)], icons: [ { icon: lineSymbol, offset: '100%', }, ], map: map, }); polyLines.push(line); } } /******* Reset the map ******/ function deleteMarkers() { for (const mapMarker of mapMarkers) { mapMarker.setMap(null); } mapMarkers = []; } function deletePolyLines() { for (const polyLine of polyLines) { polyLine.setMap(null); } polyLines = []; } function deleteEntranceMarkers() { for (const entranceMarker of entranceMarkers) { entranceMarker.setMap(null); } entranceMarkers = []; } function clearPolygons() { for (let i = 0; i < polygons.length; i++) { polygons[i].setMap(null); } polygons = []; } function resetMap(searchLocation) { if (searchLocation) { map.setCenter(searchLocation); } else { map.setCenter(GOOGLEPLEX); } map.setZoom(DEFAULT_ZOOM_LEVEL); deleteMarkers(); deletePolyLines(); deleteEntranceMarkers(); clearPolygons(); } // Initiate map & set form event handlers loadMap(); setupForm();
<html lang="en"> <head> <meta charset="utf-8"> <title>Location Selection Demo</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> </head> <body> <h1>Location Selection Demo - FindPickupPointsForLocation</h1> <div class="container"> <section class="form-container"> <form id="form-pups-for-location" name="location-selection"> <label class="form-label" for="languageCode">Language Code</label> <input type="text" id="languageCode" name="languageCode" value="en-US" /> <label class="form-label" for="regionCode">Region Code</label> <input type="text" id="regionCode" name="regionCode" value="US" /> <label class="form-label" for="searchLocation-latitude">Search Location - Latitude</label> <input type="text" id="searchLocation-latitude" name="searchLocation-latitude" value="-23.482049" /> <label class="form-label" for="searchLocation-longitude">Search Location - Longitude</label> <input type="text" id="searchLocation-longitude" name="searchLocation-longitude" value="-46.602135" /> <label class="form-label" for="orderBy">Order By</label> <select id="orderBy" name="orderBy"> <option value="DISTANCE_FROM_SEARCH_LOCATION" selected>DISTANCE_FROM_SEARCH_LOCATION</option> <option value="WALKING_ETA_FROM_SEARCH_LOCATION">WALKING_ETA_FROM_SEARCH_LOCATION</option> <option value="DRIVING_ETA_FROM_PICKUP_POINT_TO_DESTINATION">DRIVING_ETA_FROM_PICKUP_POINT_TO_DESTINATION</option> </select> <label class="form-label" for="destination-latitude">Destination - Latitude</label> <input type="text" id="destination-latitude" name="destination-latitude" value="" /> <label class="form-label" for="destination-longitude">Destination - Longitude</label> <input type="text" id="destination-longitude" name="destination-longitude" value="" /> <label class="form-label" for="maxResults">Max Results</label> <input type="number" id="maxResults" name="maxResults" min="1" value="5" step="1" /> <fieldset> <legend>Travel Modes</legend> <div> <input type="checkbox" id="walking" name="travelModes" value="WALKING" checked> <label for="walking" class="form-checkbox-label">WALKING</label> </div> <div> <input type="checkbox" id="driving" name="travelModes" value="DRIVING" checked> <label for="driving" class="form-checkbox-label">DRIVING</label> </div> <div> <input type="checkbox" id="twoWheeler" name="travelModes" value="TWO_WHEELER"> <label for="twoWheeler" class="form-checkbox-label">TWO_WHEELER</label> </div> </fieldset> <label class="form-label" for="computeWalkingEta">Compute Walking ETA</label> <select id="computeWalkingEta" name="computeWalkingEta" class="boolean"> <option value="true">true</option> <option value="false" selected>false</option> </select> <label class="form-label" for="computeDrivingEta">Compute Driving ETA</label> <select id="computeDrivingEta" name="computeDrivingEta" class="boolean"> <option value="true">true</option> <option value="false" selected>false</option> </select> <input class="submit-button" type="submit" value="Call" /> </form> </section> <section> <div id="map" class="map"></div> </section> </div> <section class="output-container"> <h2>Response</h2> <pre id="output"></pre> </section> </body> </html>
body { font-family: 'Google Sans'; } .container { display: grid; grid-template-columns: 30% 1fr; grid-template-rows: 100%; grid-column-gap: 20px; grid-row-gap: 0px; } h1 { font-size: 24px; margin-top: 20px; margin-bottom: 20px; font-weight: bold; } h2 { font-size: 18px; font-weight: bold; } h1, .form-container, .output-container { margin-left: 20px; } .map, .output-container { margin-right: 20px; } .form-container { border: 1px solid black; padding: 20px; } .map { border: 1px solid black; min-height: 800px; } .output-container { margin-top: 20px; } #output { border: 1px solid red; font-family: 'Google Sans'; min-height: 150px; } label:not(.form-checkbox-label), legend { overflow-wrap: break-word; font-weight: bold; } input:not([type="checkbox"]), select, fieldset { font-family: 'Google Sans'; width: 100%; padding: 5px 5px; margin: 0 0 20px 0; display: inline-block; border: 1px solid #ccc; border-radius: 8px; box-sizing: border-box; } input[type="submit"] { min-width: 150px; background-color: green; /* Blue */ border: none; color: white; padding: 15px 15px; text-decoration: none; display: inline-block; font-size: 16px; border-radius: 20px; width: 50%; } input[type="submit"]:hover { background-color: darkseagreen; } input[type="submit"]:active { background-color: darkseagreen; box-shadow: 0 5px #666; transform: translateY(4px); } .info-label { font-weight: bold; }
const MAPS_API_KEY = ''; // Put your API Key for Maps SDK here const LS_API_KEY = ''; // Put your API Key for Location Selection APIs here const MAPS_URL = `https://maps.googleapis.com/maps/api/js?key=${ MAPS_API_KEY}&libraries=places,geometry&callback=initMap`; const LS_BASE_URL = 'https://locationselection.googleapis.com/v1beta'; const API_URL_PUPS_FOR_PLACE = `${LS_BASE_URL}:findPickupPointsForPlace?key=${LS_API_KEY}`; const API_URL_PUPS_FOR_LOCATION = `${LS_BASE_URL}:findPickupPointsForLocation?key=${LS_API_KEY}`; const API_URL_NEARBY_PLACES = `${LS_BASE_URL}:findNearbyPlaces?key=${LS_API_KEY}`; const FORM_ID_PUPS_FOR_LOCATION = 'form-pups-for-location'; const FORM_ID_PUPS_FOR_PLACE = 'form-pups-for-place'; const FORM_ID_NEARBY_PLACES = 'form-nearby-places'; const FORM_TO_API_URL_MAP = { [FORM_ID_PUPS_FOR_LOCATION]: API_URL_PUPS_FOR_LOCATION, [FORM_ID_PUPS_FOR_PLACE]: API_URL_PUPS_FOR_PLACE, [FORM_ID_NEARBY_PLACES]: API_URL_NEARBY_PLACES, }; const RED_PIN = 'http://maps.google.com/mapfiles/ms/icons/red-dot.png'; const GREEN_PIN = 'http://maps.google.com/mapfiles/ms/icons/green-dot.png'; const BLUE_PIN = 'http://maps.google.com/mapfiles/ms/icons/blue-dot.png'; const DEFAULT_ZOOM_LEVEL = 18; // codepoint from https://fonts.google.com/icons const SEARCH_LOCATION_MARKER = '\ue7f2'; const GOOGLEPLEX = { lat: 37.422001, lng: -122.084061 }; let map; let polyLines = []; let polygons = []; let mapMarkers = []; let entranceMarkers = []; function loadMap() { const script = document.createElement('script'); script.src = MAPS_URL; document.body.appendChild(script); } function initMap() { map = new google.maps.Map( document.getElementById('map'), {center: GOOGLEPLEX, zoom: DEFAULT_ZOOM_LEVEL}); } function setupForm() { const form = document.getElementsByTagName('form')[0]; form.addEventListener('submit', onFormSubmit); } function onFormSubmit(evt) { evt.preventDefault(); evt.stopPropagation(); const formData = new FormData(evt.target); fetchAPIResults(formData); } function transformFormData(fd) { let transformedFd = { localizationPreferences: {}, }; const formId = document.getElementsByTagName('form')[0].id; if (formId === FORM_ID_PUPS_FOR_LOCATION || formId === FORM_ID_PUPS_FOR_PLACE) { transformedFd = {localizationPreferences: {}, travelModes: []}; } const addSearchLocation = () => { if (transformedFd.searchLocation == null) { transformedFd.searchLocation = {}; } }; const addDestination = () => { if (transformedFd.destination == null) { transformedFd.destination = {}; } }; fd.forEach((value, key) => { switch (key) { case 'travelModes': transformedFd.travelModes.push(value); break; case 'languageCode': transformedFd.localizationPreferences[key] = value; break; case 'regionCode': transformedFd.localizationPreferences[key] = value; break; case 'searchLocation-latitude': if (value) { addSearchLocation(); transformedFd.searchLocation['latitude'] = value; } break; case 'searchLocation-longitude': if (value) { addSearchLocation(); transformedFd.searchLocation['longitude'] = value; } break; case 'destination-latitude': if (value) { addDestination(); transformedFd.destination['latitude'] = value; } break; case 'destination-longitude': if (value) { addDestination(); transformedFd.destination['longitude'] = value; } break; default: transformedFd[key] = value; break; } }); const json = JSON.stringify(transformedFd, undefined, 2); return json; } async function fetchAPIResults(fd) { const formId = document.getElementsByTagName('form')[0].id; const url = FORM_TO_API_URL_MAP[formId]; const transformedFd = transformFormData(fd); const response = await fetch(url, {method: 'POST', body: transformedFd}); const result = await response.json(); // Display JSON displayAPIResults(result); // Update map let searchLocation = {}; if (JSON.parse(transformedFd).searchLocation) { searchLocation = { lat: Number(JSON.parse(transformedFd).searchLocation.latitude), lng: Number(JSON.parse(transformedFd).searchLocation.longitude), }; } switch (formId) { case FORM_ID_PUPS_FOR_PLACE: markPickupPointsForPlace(result); break; case FORM_ID_PUPS_FOR_LOCATION: markPickupPointsForLocation(result, searchLocation); break; case FORM_ID_NEARBY_PLACES: markNearbyPlaces(result, searchLocation); break; default: break; } } function displayAPIResults(data) { const output = document.getElementById('output'); output.textContent = JSON.stringify(data, undefined, 2); } function markNearbyPlaces(data, searchLocation) { if (data.error) { resetMap(); return; } const places = []; for (const placeResult of data.placeResults) { places.push(placeResult.place); } resetMap(searchLocation); markPlaces(places, searchLocation); for (const place of places) { markEntrances(place.associatedCompounds, place); } markSearchLocation(searchLocation, ''); for (const place of places) { mapPolygons(place.associatedCompounds); } } function markPickupPointsForPlace(data) { if (data.error) { resetMap(); return; } const place = data.placeResult.place; const pickupPoints = data.pickupPointResults; const searchLocation = { lat: place.geometry.location.latitude, lng: place.geometry.location.longitude }; resetMap(searchLocation); markPickupPoints(place, pickupPoints, searchLocation); markEntrances(place.associatedCompounds, place); markSearchLocation(searchLocation, place.displayName); createPolyLinesOneToMany(searchLocation, pickupPoints); mapPolygons(place.associatedCompounds); } function markPickupPointsForLocation(data, searchLocation) { if (data.error) { resetMap(); return; } const placeIdToPlace = {}; // A dict, and the key is placeId(str)s and the value is a list of pups. const placePickupPoints = {}; data.placeResults.forEach(result => { placeIdToPlace[result.place.placeId] = result.place; placePickupPoints[result.place.placeId] = []; }); data.placePickupPointResults.forEach(result => { placePickupPoints[result.associatedPlaceId].push(result.pickupPointResult); }) resetMap(searchLocation); for (const placeId in placePickupPoints) { const place = placeIdToPlace[placeId]; const pups = placePickupPoints[placeId]; markEntrances(place.associatedCompounds, place); markPickupPoints(place, pups, searchLocation); createPolyLinesOneToMany(searchLocation, pups); mapPolygons(place.associatedCompounds); } // update the marker rank to global order for (let i = 0; i < mapMarkers.length; i++) { mapMarkers[i].label = String(i); } markSearchLocation(searchLocation, ''); } function markPlaces(places, searchLocation) { for (const place of places) { const placeLocation = place.geometry.location; const infoWindow = new google.maps.InfoWindow({content: createInfoWindow(place, null)}); const marker = new google.maps.Marker({ position: toLatLngLiteral(placeLocation), animation: google.maps.Animation.DROP, map: map, }); marker.addListener('click', () => { infoWindow.open(map, marker); }); map.addListener('click', () => { infoWindow.close(); }); mapMarkers.push(marker); } } function markEntrances(compounds, place) { if (!compounds) { return; } for (const compound of compounds) { if (!compound.entrances) { continue; } for (const entrance of compound.entrances) { const entranceMarker = new google.maps.Marker({ position: toLatLngLiteral(entrance.location), icon: { url: BLUE_PIN, }, animation: google.maps.Animation.DROP, map: map, }); const infoWindow = new google.maps.InfoWindow({content: createInfoWindow(place, null)}); entranceMarker.addListener('click', () => { infoWindow.open(map, entranceMarker); }); map.addListener('click', () => { infoWindow.close(); }); entranceMarkers.push(entranceMarker); } } } function mapPolygons(many) { if (!many) { return; } for (const toPoint of many) { const data = toPoint.geometry.displayBoundary; if (data == null || data.coordinates == null) { continue; } const value = data.coordinates; const polyArray = JSON.parse(JSON.stringify(value))[0]; const usedColors = []; const finalLatLngs = []; let color = ''; for (let i = 0; i < polyArray.length; ++i) { if (polyArray[i] != null && polyArray[i].length > 0) { color = getColor(usedColors); usedColors.push(color); if (isArrLatLng(polyArray[i])) { finalLatLngs.push({lat: polyArray[i][1], lng: polyArray[i][0]}); } } } const poly = new google.maps.Polygon({ strokeColor: color, strokeOpacity: 0.2, strokeWeight: 5, fillColor: color, fillOpacity: 0.1, paths: finalLatLngs, map: map, }); polygons.push(poly); } } function getColor(usedColors) { let color = generateStrokeColor(); while (usedColors.includes(color)) { color = generateStrokeColor(); } return color; } function generateStrokeColor() { return Math.floor(Math.random() * 16777215).toString(16); } function isArrLatLng(currArr) { if (!currArr || currArr.length !== 2) { return false; } return ((typeof currArr[0]) === 'number') && ((typeof currArr[1]) === 'number'); } function toLatLngLiteral(latlng) { return {lat: latlng.latitude, lng: latlng.longitude}; } function pickupHasRestrictions(pickupPointData) { let hasRestrictions = false; const travelDetails = pickupPointData.travelDetails; for (let i = 0; i < travelDetails.length; i++) { if (travelDetails[i].trafficRestriction !== 'NO_RESTRICTION') { hasRestrictions = true; } } return hasRestrictions; } function markPickupPoints(place, pickupPoints, searchLocation) { for (let i = 0; i < pickupPoints.length; i++) { const pickupPointData = pickupPoints[i]; const pickupPoint = pickupPoints[i].pickupPoint; const pupIcon = pickupHasRestrictions(pickupPointData) ? RED_PIN : GREEN_PIN; const contentString = createInfoWindow(place, pickupPoint); const pupInfoWindow = new google.maps.InfoWindow({content: contentString}); const marker = new google.maps.Marker({ position: toLatLngLiteral(pickupPoint.location), label: { text: String(i), fontWeight: 'bold', fontSize: '20px', color: '#000' }, animation: google.maps.Animation.DROP, map, icon: { url: pupIcon, anchor: new google.maps.Point(14, 43), labelOrigin: new google.maps.Point(-5, 5) }, }); marker.addListener('click', () => { pupInfoWindow.open(map, marker); }); map.addListener('click', () => { pupInfoWindow.close(); }); mapMarkers.push(marker); } } function createInfoWindow(place, pickupPoint) { let result = []; const addResult = (value, key, map) => result.push(`<p><span class="info-label">${key}:</span> ${value}</p>`); const formatAddress = (address) => address.lines.join(','); const placeFieldMap = new Map(); if (place !== null) { placeFieldMap.set('Place', ''); placeFieldMap.set('Name', place.displayName); placeFieldMap.set('Place ID', place.placeId); placeFieldMap.set('Address', formatAddress(place.address.formattedAddress)); } const pickupPointFieldMap = new Map(); if (pickupPoint !== null) { pickupPointFieldMap.set('Pickup point', ''); pickupPointFieldMap.set('Name', pickupPoint.displayName); } placeFieldMap.forEach(addResult); result.push('<hr/>'); pickupPointFieldMap.forEach(addResult); return result.join(''); } function markSearchLocation(location, label) { const infoWindow = new google.maps.InfoWindow({content: `<p><b>Name: </b>${label}</p>`}); const marker = new google.maps.Marker({ position: location, map, label: { text: SEARCH_LOCATION_MARKER, fontFamily: 'Material Icons', color: '#ffffff', fontSize: '18px', fontWeight: 'bold', }, }); marker.addListener('click', () => { infoWindow.open(map, marker); }); map.addListener('click', () => { infoWindow.close(); }); mapMarkers.push(marker); } function createPolyLinesOneToMany(one, many) { const lineSymbol = { path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW, }; for (const toPoint of many) { const line = new google.maps.Polyline({ path: [one, toLatLngLiteral(toPoint.pickupPoint.location)], icons: [ { icon: lineSymbol, offset: '100%', }, ], map: map, }); polyLines.push(line); } } /******* Reset the map ******/ function deleteMarkers() { for (const mapMarker of mapMarkers) { mapMarker.setMap(null); } mapMarkers = []; } function deletePolyLines() { for (const polyLine of polyLines) { polyLine.setMap(null); } polyLines = []; } function deleteEntranceMarkers() { for (const entranceMarker of entranceMarkers) { entranceMarker.setMap(null); } entranceMarkers = []; } function clearPolygons() { for (let i = 0; i < polygons.length; i++) { polygons[i].setMap(null); } polygons = []; } function resetMap(searchLocation) { if (searchLocation) { map.setCenter(searchLocation); } else { map.setCenter(GOOGLEPLEX); } map.setZoom(DEFAULT_ZOOM_LEVEL); deleteMarkers(); deletePolyLines(); deleteEntranceMarkers(); clearPolygons(); } // Initiate map & set form event handlers loadMap(); setupForm();
<html lang="en"> <head> <meta charset="utf-8"> <title>Location Selection Demo</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> </head> <body> <h1>Location Selection Demo - FindNearbyPlaces</h1> <div class="container"> <section class="form-container"> <form id="form-nearby-places" name="location-selection"> <label class="form-label" for="languageCode">Language Code</label> <input type="text" id="languageCode" name="languageCode" value="en-US" /> <label class="form-label" for="regionCode">Region Code</label> <input type="text" id="regionCode" name="regionCode" value="US" /> <label class="form-label" for="searchLocation-latitude">Search Location - Latitude</label> <input type="text" id="searchLocation-latitude" name="searchLocation-latitude" value="37.365647" /> <label class="form-label" for="searchLocation-longitude">Search Location - Longitude</label> <input type="text" id="searchLocation-longitude" name="searchLocation-longitude" value="-121.925356" /> <label class="form-label" for="maxResults">Max Results</label> <input type="number" id="maxResults" name="maxResults" min="1" value="5" step="1" /> <input class="submit-button" type="submit" value="Call" /> </form> </section> <section> <div id="map" class="map"></div> </section> </div> <section class="output-container"> <h2>Response</h2> <pre id="output"></pre> </section> </body> </html>
body { font-family: 'Google Sans'; } .container { display: grid; grid-template-columns: 30% 1fr; grid-template-rows: 100%; grid-column-gap: 20px; grid-row-gap: 0px; } h1 { font-size: 24px; margin-top: 20px; margin-bottom: 20px; font-weight: bold; } h2 { font-size: 18px; font-weight: bold; } h1, .form-container, .output-container { margin-left: 20px; } .map, .output-container { margin-right: 20px; } .form-container { border: 1px solid black; padding: 20px; } .map { border: 1px solid black; min-height: 800px; } .output-container { margin-top: 20px; } #output { border: 1px solid red; font-family: 'Google Sans'; min-height: 150px; } label:not(.form-checkbox-label), legend { overflow-wrap: break-word; font-weight: bold; } input:not([type="checkbox"]), select, fieldset { font-family: 'Google Sans'; width: 100%; padding: 5px 5px; margin: 0 0 20px 0; display: inline-block; border: 1px solid #ccc; border-radius: 8px; box-sizing: border-box; } input[type="submit"] { min-width: 150px; background-color: green; /* Blue */ border: none; color: white; padding: 15px 15px; text-decoration: none; display: inline-block; font-size: 16px; border-radius: 20px; width: 50%; } input[type="submit"]:hover { background-color: darkseagreen; } input[type="submit"]:active { background-color: darkseagreen; box-shadow: 0 5px #666; transform: translateY(4px); } .info-label { font-weight: bold; }
const MAPS_API_KEY = ''; // Put your API Key for Maps SDK here const LS_API_KEY = ''; // Put your API Key for Location Selection APIs here const MAPS_URL = `https://maps.googleapis.com/maps/api/js?key=${ MAPS_API_KEY}&libraries=places,geometry&callback=initMap`; const LS_BASE_URL = 'https://locationselection.googleapis.com/v1beta'; const API_URL_PUPS_FOR_PLACE = `${LS_BASE_URL}:findPickupPointsForPlace?key=${LS_API_KEY}`; const API_URL_PUPS_FOR_LOCATION = `${LS_BASE_URL}:findPickupPointsForLocation?key=${LS_API_KEY}`; const API_URL_NEARBY_PLACES = `${LS_BASE_URL}:findNearbyPlaces?key=${LS_API_KEY}`; const FORM_ID_PUPS_FOR_LOCATION = 'form-pups-for-location'; const FORM_ID_PUPS_FOR_PLACE = 'form-pups-for-place'; const FORM_ID_NEARBY_PLACES = 'form-nearby-places'; const FORM_TO_API_URL_MAP = { [FORM_ID_PUPS_FOR_LOCATION]: API_URL_PUPS_FOR_LOCATION, [FORM_ID_PUPS_FOR_PLACE]: API_URL_PUPS_FOR_PLACE, [FORM_ID_NEARBY_PLACES]: API_URL_NEARBY_PLACES, }; const RED_PIN = 'http://maps.google.com/mapfiles/ms/icons/red-dot.png'; const GREEN_PIN = 'http://maps.google.com/mapfiles/ms/icons/green-dot.png'; const BLUE_PIN = 'http://maps.google.com/mapfiles/ms/icons/blue-dot.png'; const DEFAULT_ZOOM_LEVEL = 18; // codepoint from https://fonts.google.com/icons const SEARCH_LOCATION_MARKER = '\ue7f2'; const GOOGLEPLEX = { lat: 37.422001, lng: -122.084061 }; let map; let polyLines = []; let polygons = []; let mapMarkers = []; let entranceMarkers = []; function loadMap() { const script = document.createElement('script'); script.src = MAPS_URL; document.body.appendChild(script); } function initMap() { map = new google.maps.Map( document.getElementById('map'), {center: GOOGLEPLEX, zoom: DEFAULT_ZOOM_LEVEL}); } function setupForm() { const form = document.getElementsByTagName('form')[0]; form.addEventListener('submit', onFormSubmit); } function onFormSubmit(evt) { evt.preventDefault(); evt.stopPropagation(); const formData = new FormData(evt.target); fetchAPIResults(formData); } function transformFormData(fd) { let transformedFd = { localizationPreferences: {}, }; const formId = document.getElementsByTagName('form')[0].id; if (formId === FORM_ID_PUPS_FOR_LOCATION || formId === FORM_ID_PUPS_FOR_PLACE) { transformedFd = {localizationPreferences: {}, travelModes: []}; } const addSearchLocation = () => { if (transformedFd.searchLocation == null) { transformedFd.searchLocation = {}; } }; const addDestination = () => { if (transformedFd.destination == null) { transformedFd.destination = {}; } }; fd.forEach((value, key) => { switch (key) { case 'travelModes': transformedFd.travelModes.push(value); break; case 'languageCode': transformedFd.localizationPreferences[key] = value; break; case 'regionCode': transformedFd.localizationPreferences[key] = value; break; case 'searchLocation-latitude': if (value) { addSearchLocation(); transformedFd.searchLocation['latitude'] = value; } break; case 'searchLocation-longitude': if (value) { addSearchLocation(); transformedFd.searchLocation['longitude'] = value; } break; case 'destination-latitude': if (value) { addDestination(); transformedFd.destination['latitude'] = value; } break; case 'destination-longitude': if (value) { addDestination(); transformedFd.destination['longitude'] = value; } break; default: transformedFd[key] = value; break; } }); const json = JSON.stringify(transformedFd, undefined, 2); return json; } async function fetchAPIResults(fd) { const formId = document.getElementsByTagName('form')[0].id; const url = FORM_TO_API_URL_MAP[formId]; const transformedFd = transformFormData(fd); const response = await fetch(url, {method: 'POST', body: transformedFd}); const result = await response.json(); // Display JSON displayAPIResults(result); // Update map let searchLocation = {}; if (JSON.parse(transformedFd).searchLocation) { searchLocation = { lat: Number(JSON.parse(transformedFd).searchLocation.latitude), lng: Number(JSON.parse(transformedFd).searchLocation.longitude), }; } switch (formId) { case FORM_ID_PUPS_FOR_PLACE: markPickupPointsForPlace(result); break; case FORM_ID_PUPS_FOR_LOCATION: markPickupPointsForLocation(result, searchLocation); break; case FORM_ID_NEARBY_PLACES: markNearbyPlaces(result, searchLocation); break; default: break; } } function displayAPIResults(data) { const output = document.getElementById('output'); output.textContent = JSON.stringify(data, undefined, 2); } function markNearbyPlaces(data, searchLocation) { if (data.error) { resetMap(); return; } const places = []; for (const placeResult of data.placeResults) { places.push(placeResult.place); } resetMap(searchLocation); markPlaces(places, searchLocation); for (const place of places) { markEntrances(place.associatedCompounds, place); } markSearchLocation(searchLocation, ''); for (const place of places) { mapPolygons(place.associatedCompounds); } } function markPickupPointsForPlace(data) { if (data.error) { resetMap(); return; } const place = data.placeResult.place; const pickupPoints = data.pickupPointResults; const searchLocation = { lat: place.geometry.location.latitude, lng: place.geometry.location.longitude }; resetMap(searchLocation); markPickupPoints(place, pickupPoints, searchLocation); markEntrances(place.associatedCompounds, place); markSearchLocation(searchLocation, place.displayName); createPolyLinesOneToMany(searchLocation, pickupPoints); mapPolygons(place.associatedCompounds); } function markPickupPointsForLocation(data, searchLocation) { if (data.error) { resetMap(); return; } const placeIdToPlace = {}; // A dict, and the key is placeId(str)s and the value is a list of pups. const placePickupPoints = {}; data.placeResults.forEach(result => { placeIdToPlace[result.place.placeId] = result.place; placePickupPoints[result.place.placeId] = []; }); data.placePickupPointResults.forEach(result => { placePickupPoints[result.associatedPlaceId].push(result.pickupPointResult); }) resetMap(searchLocation); for (const placeId in placePickupPoints) { const place = placeIdToPlace[placeId]; const pups = placePickupPoints[placeId]; markEntrances(place.associatedCompounds, place); markPickupPoints(place, pups, searchLocation); createPolyLinesOneToMany(searchLocation, pups); mapPolygons(place.associatedCompounds); } // update the marker rank to global order for (let i = 0; i < mapMarkers.length; i++) { mapMarkers[i].label = String(i); } markSearchLocation(searchLocation, ''); } function markPlaces(places, searchLocation) { for (const place of places) { const placeLocation = place.geometry.location; const infoWindow = new google.maps.InfoWindow({content: createInfoWindow(place, null)}); const marker = new google.maps.Marker({ position: toLatLngLiteral(placeLocation), animation: google.maps.Animation.DROP, map: map, }); marker.addListener('click', () => { infoWindow.open(map, marker); }); map.addListener('click', () => { infoWindow.close(); }); mapMarkers.push(marker); } } function markEntrances(compounds, place) { if (!compounds) { return; } for (const compound of compounds) { if (!compound.entrances) { continue; } for (const entrance of compound.entrances) { const entranceMarker = new google.maps.Marker({ position: toLatLngLiteral(entrance.location), icon: { url: BLUE_PIN, }, animation: google.maps.Animation.DROP, map: map, }); const infoWindow = new google.maps.InfoWindow({content: createInfoWindow(place, null)}); entranceMarker.addListener('click', () => { infoWindow.open(map, entranceMarker); }); map.addListener('click', () => { infoWindow.close(); }); entranceMarkers.push(entranceMarker); } } } function mapPolygons(many) { if (!many) { return; } for (const toPoint of many) { const data = toPoint.geometry.displayBoundary; if (data == null || data.coordinates == null) { continue; } const value = data.coordinates; const polyArray = JSON.parse(JSON.stringify(value))[0]; const usedColors = []; const finalLatLngs = []; let color = ''; for (let i = 0; i < polyArray.length; ++i) { if (polyArray[i] != null && polyArray[i].length > 0) { color = getColor(usedColors); usedColors.push(color); if (isArrLatLng(polyArray[i])) { finalLatLngs.push({lat: polyArray[i][1], lng: polyArray[i][0]}); } } } const poly = new google.maps.Polygon({ strokeColor: color, strokeOpacity: 0.2, strokeWeight: 5, fillColor: color, fillOpacity: 0.1, paths: finalLatLngs, map: map, }); polygons.push(poly); } } function getColor(usedColors) { let color = generateStrokeColor(); while (usedColors.includes(color)) { color = generateStrokeColor(); } return color; } function generateStrokeColor() { return Math.floor(Math.random() * 16777215).toString(16); } function isArrLatLng(currArr) { if (!currArr || currArr.length !== 2) { return false; } return ((typeof currArr[0]) === 'number') && ((typeof currArr[1]) === 'number'); } function toLatLngLiteral(latlng) { return {lat: latlng.latitude, lng: latlng.longitude}; } function pickupHasRestrictions(pickupPointData) { let hasRestrictions = false; const travelDetails = pickupPointData.travelDetails; for (let i = 0; i < travelDetails.length; i++) { if (travelDetails[i].trafficRestriction !== 'NO_RESTRICTION') { hasRestrictions = true; } } return hasRestrictions; } function markPickupPoints(place, pickupPoints, searchLocation) { for (let i = 0; i < pickupPoints.length; i++) { const pickupPointData = pickupPoints[i]; const pickupPoint = pickupPoints[i].pickupPoint; const pupIcon = pickupHasRestrictions(pickupPointData) ? RED_PIN : GREEN_PIN; const contentString = createInfoWindow(place, pickupPoint); const pupInfoWindow = new google.maps.InfoWindow({content: contentString}); const marker = new google.maps.Marker({ position: toLatLngLiteral(pickupPoint.location), label: { text: String(i), fontWeight: 'bold', fontSize: '20px', color: '#000' }, animation: google.maps.Animation.DROP, map, icon: { url: pupIcon, anchor: new google.maps.Point(14, 43), labelOrigin: new google.maps.Point(-5, 5) }, }); marker.addListener('click', () => { pupInfoWindow.open(map, marker); }); map.addListener('click', () => { pupInfoWindow.close(); }); mapMarkers.push(marker); } } function createInfoWindow(place, pickupPoint) { let result = []; const addResult = (value, key, map) => result.push(`<p><span class="info-label">${key}:</span> ${value}</p>`); const formatAddress = (address) => address.lines.join(','); const placeFieldMap = new Map(); if (place !== null) { placeFieldMap.set('Place', ''); placeFieldMap.set('Name', place.displayName); placeFieldMap.set('Place ID', place.placeId); placeFieldMap.set('Address', formatAddress(place.address.formattedAddress)); } const pickupPointFieldMap = new Map(); if (pickupPoint !== null) { pickupPointFieldMap.set('Pickup point', ''); pickupPointFieldMap.set('Name', pickupPoint.displayName); } placeFieldMap.forEach(addResult); result.push('<hr/>'); pickupPointFieldMap.forEach(addResult); return result.join(''); } function markSearchLocation(location, label) { const infoWindow = new google.maps.InfoWindow({content: `<p><b>Name: </b>${label}</p>`}); const marker = new google.maps.Marker({ position: location, map, label: { text: SEARCH_LOCATION_MARKER, fontFamily: 'Material Icons', color: '#ffffff', fontSize: '18px', fontWeight: 'bold', }, }); marker.addListener('click', () => { infoWindow.open(map, marker); }); map.addListener('click', () => { infoWindow.close(); }); mapMarkers.push(marker); } function createPolyLinesOneToMany(one, many) { const lineSymbol = { path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW, }; for (const toPoint of many) { const line = new google.maps.Polyline({ path: [one, toLatLngLiteral(toPoint.pickupPoint.location)], icons: [ { icon: lineSymbol, offset: '100%', }, ], map: map, }); polyLines.push(line); } } /******* Reset the map ******/ function deleteMarkers() { for (const mapMarker of mapMarkers) { mapMarker.setMap(null); } mapMarkers = []; } function deletePolyLines() { for (const polyLine of polyLines) { polyLine.setMap(null); } polyLines = []; } function deleteEntranceMarkers() { for (const entranceMarker of entranceMarkers) { entranceMarker.setMap(null); } entranceMarkers = []; } function clearPolygons() { for (let i = 0; i < polygons.length; i++) { polygons[i].setMap(null); } polygons = []; } function resetMap(searchLocation) { if (searchLocation) { map.setCenter(searchLocation); } else { map.setCenter(GOOGLEPLEX); } map.setZoom(DEFAULT_ZOOM_LEVEL); deleteMarkers(); deletePolyLines(); deleteEntranceMarkers(); clearPolygons(); } // Initiate map & set form event handlers loadMap(); setupForm();
قبل استخدام واجهة برمجة التطبيقات Location Selection API للبحث عن نقاط استلام الرحلات، اتّبِع التعليمات الواردة هنا لدمجها مع مكتبة العملاء.
توفّر خدمة "اختيار الموقع الجغرافي" ثلاث واجهات برمجة تطبيقات لاختيار الاستلام والتسليم، وهي: FindNearbyPlaces
وFindPickupPointsForPlace
وFindPickupPointsForLocation
.
استخدِم FindNearbyPlaces
لاسترجاع أماكن بالقرب من الموقع الجغرافي في خدمة البحث أو الجهاز. يتم ترتيب الأماكن حسب القرب من الموقع وبروزها في مشاركة الرحلات. تعرض دالة FindNearbyPlaces
قائمة بالأماكن التي يمكن عرضها
للسماح للمستخدم بالاختيار الأفضل. بعد اختيار مكان، استخدِم
FindPickupPointsForPlace
لجلب نقاط الاستلام للمكان المحدّد.
استخدِم FindPickupPointsForLocation
لاسترجاع الأماكن ونقاط الاستلام المرتبطة بها بالقرب من الموقع الجغرافي للبحث أو الجهاز في مكالمة RPC نفسها. ترتبط كل نقطة استلام
بمكان. يتم ترتيب نقاط الاستلام حسب القرب من
الموقع وبروزها في مشاركة الرحلات. لاحظ أنه يتم ترتيب نقاط الاستلام لأماكن متعددة معًا. يجمع FindPickupPointsForLocation
بين
FindNearbyPlaces
وFindPickupPointsForPlace
. على سبيل المثال، لنفترض أن موقع الطلب
بالقرب من المكان P1 وP2 وP3. إذا كانت أفضل نقطة استلام، T1 مرتبطة بالمكان P2، وكانت أفضل نقطة استلام بعد ذلك مرتبطة بالمكان P1، ستحتوي النتائج على الترتيب [T1:P2, T2:P1, ...].
يعتمد ترتيب نقاط الاستلام على المعايير الواردة في الطلب. لمزيد من المعلومات، يمكنك الاطّلاع على المقالة تحسين عمليات استلام الطلبات لرحلات متعددة.
البحث حسب الموقع الجغرافي
إذا كنت تفضل عرض الأماكن للمستخدم قبل تحديد نقاط الاستلام أو عرض الأماكن المجاورة عن طريق سحب الدبوس، فاستخدم طلب استدعاء إجراء عن بُعد التالي:
FindNearbyPlacesRequest find_nearby_places_request =
FindNearbyPlacesRequest.newBuilder()
.setLocalizationPreferences(LocalizationPreferences.newBuilder()
// Language used for localizing text such as name or address.
.setLanguageCode("en")
.setRegionCode("US")
.build())
// Rider's location or location of dragged pin.
.setSearchLocation(LatLng.newBuilder().setLatitude(37.365647).setLongitude(-121.925356))
// Number of places requested.
.setMaxResults(3)
.build();
FindNearbyPlacesResponse findNearbyPlacesResponse =
locationSelectionBetaClient.findNearbyPlaces(find_nearby_places_request);
يعرض استدعاء استدعاء إجراء عن بُعد (RPC) قائمة مرتبة من استجابات الأماكن التي تفي بمعايير الإدخال، مرتبة حسب التقارب والبروز. يمكنك السماح للراكب باختيار مكان أو استخدام النتيجة الأولى والمتابعة إلى تحديد نقطة الاستلام. يتضمّن كل ردّ خاص بالمكان سمة place_id
فريدة يمكن استخدامها في FindPickupPointsForPlaceRequest
لجلب نقاط الاستلام. لمزيد من المعلومات، راجع البحث حسب رقم تعريف المكان.
FindPickupPointsForLocationRequest FindPickupPointsForLocationRequest =
FindPickupPointsForLocationRequest.newBuilder()
// Language used for localizing text such as name and address.
.setLocalizationPreferences(LocalizationPreferences.newBuilder().setRegionCode("US").setLanguageCode("en"))
// The search location of the rider or the dropped pin.
.setSearchLocation(LatLng.newBuilder().setLatitude(-23.482049).setLongitude(-46.602135))
// The max results returned.
.setMaxResults(5)
// List of travel modes. At least one of the travel modes must be supported by the pickup points.
.addTravelModes(TravelMode.DRIVING)
// Specifies the sorting order of matching pickup points.
.setOrderBy(PickupPointOrder.DISTANCE_FROM_SEARCH_LOCATION)
.build();
FindPickupPointsForLocationResponse FindPickupPointsForLocationResponse =
locationSelectionService.FindPickupPointsForLocation(
RpcClientContext.create(), FindPickupPointsForLocationRequest);
يعرض استدعاء استدعاء إجراء عن بُعد (RPC) قائمة مرتبة بنقاط الاستلام التي تفي بمعايير الإدخال، مرتبة حسب التقارب والشهرة. يجمع طلب استدعاء إجراء عن بُعد هذا FindNearbyPlaces
وFindPickupPointsForPlaceRequest
ويمكن استخدامه بدلاً من الجمع بين المكالمتين الأخريين.
البحث حسب رقم تعريف المكان
يمكنك الحصول على place_id
باستخدام FindNearbyPlaces
أو باستخدام خدمة الإكمال التلقائي في Places API. ثم استخدم استدعاء RPC التالي لتوفير نقاط
الاستلام المثلى للمكان المحدد:
FindPickupPointsForPlaceRequest findPickupPointsForPlaceRequest =
FindPickupPointsForPlaceRequest.newBuilder()
// Language used for localizing text such as name and address.
.setLocalizationPreferences(LocalizationPreferences.newBuilder().setRegionCode("US").setLanguageCode("en"))
// Place ID of the place for which pickup points are being fetched;
// for example, Hilton Hotel, Downtown San Jose.
.setPlaceId("ChIJwTUa-q_Mj4ARff4yludGH-M")
// List of travel modes. At least one of the travel modes must be supported by the pickup points.
.addTravelModes(TravelMode.DRIVING)
// Rider's location or location of dragged pin.
// It is recommended to use the same location that was used in `FindNearbyPlaces` for better quality.
.setSearchLocation(LatLng.newBuilder().setLatitude(37.329472).setLongitude(-121.890449))
.setOrderBy(PickupPointOrder.DISTANCE_FROM_SEARCH_LOCATION)
.setMaxResults(5)
.build();
FindPickupPointsForPlaceResponse findPickupPointsForPlaceResponse =
locationSelectionBetaClient.findPickupPointsForPlace(findPickupPointsForPlaceRequest);
تعرض الدالة FindPickupPointsForPlace
القيمة PickupPointResponses
بخطوط خطوط الطول/العرض للنقاط التي يمكن فيها التقاط راكب.
تحسين عمليات استلام الطلب لرحلات متعددة
تتيح خدمة FindPickupPointsForPlace
طلب نقاط الاستلام بحلول DRIVING_ETA_FROM_PICKUP_POINT_TO_DESTINATION
للرحلات المشتركة أو المتتالية. يتيح لك هذا إرجاع نقطة استلام للرحلة التالية المحسَّنة بمسار الرحلة الحالي للسائق.
مثال
FindPickupPointsForPlaceRequest findPickupPointsForPlaceRequest =
FindPickupPointsForPlaceRequest.newBuilder()
// Language used for localizing text such as name and address.
.setLocalizationPreferences(LocalizationPreferences.newBuilder().setRegionCode("US").setLanguageCode("en"))
// Place ID of the place for which pickup points are being fetched;
// for example, Hilton Hotel, Downtown San Jose.
.setPlaceId("ChIJwTUa-q_Mj4ARff4yludGH-M")
// List of travel modes. At least one of the travel modes must be supported by the pickup points.
.addTravelModes(TravelMode.DRIVING)
// Second rider's location or location of dragged pin.
.setSearchLocation(LatLng.newBuilder().setLatitude(37.329472).setLongitude(-121.890449))
// Location of the driver's next drop off after picking up the second
// rider. Note, it is not necessarily the second rider's destination.
.setDestination(LatLng.newBuilder().setLatitude(37.329472).setLongitude(-121.890449))
.setOrderBy(PickupPointOrder.DRIVING_ETA_FROM_PICKUP_POINT_TO_DESTINATION)
.setComputeDrivingEta(true)
.setMaxResults(5)
.build();
FindPickupPointsForPlaceResponse findPickupPointsForPlaceResponse =
locationSelectionBetaClient.findPickupPointsForPlace(findPickupPointsForPlaceRequest);
عرض مخططات المباني ومداخلها ومخارجها
في رسالة الرد على النموذج الأولي للمكان، يحدّد الحقل associatedCompounds
المركبات المرتبطة بالمكان. تحتوي الرسالة المركبة على
ثلاثة أنواع من المعلومات:
- النوع المركّب: أحد الخيارات الأربعة
compoundBuilding
- مبنى واحد مستقل، مثل مركز تسوق أو سوبرماركت.compoundSection
- مجمّع داخل مجمّع أكبر، مثل متجر واحد في مركز تجاري.compoundGrounds
- كل ما هو مرتبط بالمكانcompoundBuilding
، مثل مركز تجاري وموقف للسيارات وأي مبانٍ أخرى داخل موقف السيارات هذا.unrecognized
- القيمة التلقائية
- الهندسة - إحداثيات المضلّع المحدد، المخزنة في هيكل GeoJSON في حقل
displayBoundary
. ويتم استخدام هذه الإحداثيات لإنشاء مخطط القسم أو المبنى أو الأراضي. - المداخل - إحداثيات خط العرض وخط الطول لجميع المداخل والخروج الموجودة.
تعرض اختيار الموقع أي نوع مركّب مرتبط بموقع البحث. إذا كان موقع البحث موجودًا في متجر معين في مركز تجاري، فإن "تحديد الموقع" يعرض: * المتجر المحدد، ومداخله ومخارجه، ومخطط المتجر * المبنى المركّب (المركز التجاري)، ومداخله ومخارجه، ومخطط المركز التجاري * أراضي المُجمّع (المركز التجاري + موقف السيارات)، ومداخله ومخارجه ومخطط الأرض بالكامل
تُظهر هذه الصورة جميع الأنواع الثلاثة المركّبة التي يتم عرضها.
تُظهر هذه الصورة عدة مواقع جغرافية داخل مطار، مع حدود مبنى داخل المطار وحدوده وجميع الأراضي المرتبطة به.