Rechercher des points de prise en charge des courses à l'aide de la sélection de l'emplacement (bêta)


   
        <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();


    

Avant d'utiliser l'API Location Selection pour rechercher des points de retrait, suivez ces instructions pour intégrer la bibliothèque cliente.

Le service de sélection d'emplacements fournit trois API pour la sélection des lieux de prise en charge et de dépose: FindNearbyPlaces, FindPickupPointsForPlace et FindPickupPointsForLocation.

Utilisez FindNearbyPlaces pour extraire des lieux à proximité de la position de l'appareil ou de la recherche. Les lieux sont classés en fonction de leur proximité par rapport à l'emplacement et de leur proéminence pour le partage de course. FindNearbyPlaces renvoie la liste des lieux pouvant être affichés pour permettre à l'utilisateur de faire la meilleure sélection. Une fois qu'un lieu est sélectionné, utilisez FindPickupPointsForPlace pour récupérer les points de retrait du lieu sélectionné.

Utilisez FindPickupPointsForLocation pour récupérer les lieux et les points de retrait associés à proximité de la recherche ou de la position de l'appareil dans le même appel RPC. Chaque point retrait est associé à un lieu. Les points de prise en charge sont classés en fonction de leur proximité avec l'établissement et de leur proéminence pour le partage de course. Notez que les points retrait de plusieurs lieux sont commandés ensemble. FindPickupPointsForLocation combine FindNearbyPlaces et FindPickupPointsForPlace. Par exemple, supposons que l'emplacement de la requête se trouve à proximité des lieux P1, P2 et P3. Si le meilleur point de retrait T1 est associé au lieu P2 et que le prochain meilleur point de retrait est associé au lieu P1, les résultats indiqueront le tri [T1:P2, T2:P1, ...].

Le classement des points retrait dépend des critères fournis dans la demande. Pour en savoir plus, consultez la section Optimiser la prise en charge pour plusieurs trajets.

Recherche par lieu

Si vous préférez afficher les lieux à l'utilisateur avant la sélection des points de retrait ou afficher les lieux à proximité en faisant glisser un repère, utilisez l'appel RPC suivant:

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);

L'appel RPC renvoie une liste classée de réponses de lieu qui répondent aux critères d'entrée, classées selon une combinaison de proximité et de proéminence. Vous pouvez laisser le passager choisir un lieu ou utiliser le premier résultat et procéder à la sélection du point de prise en charge. Chaque réponse de lieu possède un place_id unique qui peut être utilisé dans FindPickupPointsForPlaceRequest pour récupérer les points de retrait. Pour en savoir plus, consultez Rechercher par ID de lieu.

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);

L'appel RPC renvoie une liste classée des points de prise en charge qui répondent aux critères d'entrée, triés en fonction de la proximité et de la proéminence. Cet appel RPC combine FindNearbyPlaces et FindPickupPointsForPlaceRequest et peut être utilisé au lieu de combiner les deux autres appels.

Rechercher par ID de lieu

Vous pouvez obtenir une place_id à l'aide de FindNearbyPlaces ou du service Autocomplete de l'API Places. Utilisez ensuite l'appel RPC suivant pour fournir les points de retrait optimaux pour le lieu spécifié:

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 renvoie PickupPointResponses avec lat/lngs pour les points où un usager peut être pris en charge.

Optimiser les retraits pour plusieurs trajets

Pour les trajets partagés ou consécutifs, FindPickupPointsForPlace permet de commander les points de retrait avant le DRIVING_ETA_FROM_PICKUP_POINT_TO_DESTINATION. Cela vous permet de renvoyer un point de départ pour le prochain trajet qui est optimisé en fonction de l'itinéraire actuel du conducteur.

Exemple

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);

Affichage des contours, des entrées et des sorties des bâtiments

Dans le message de réponse proto de lieu, le champ associatedCompounds identifie les composés associés au lieu. Le message composé contient trois types d'informations:

  • Type composé : l'une des quatre options suivantes
    • compoundBuilding : bâtiment autonome unique, tel qu'un centre commercial ou un supermarché.
    • compoundSection : complexe au sein d'un complexe plus grand, comme un magasin dans un centre commercial.
    • compoundGrounds : tout ce qui est associé à un compoundBuilding, comme un centre commercial, son parking et tous les autres bâtiments situés dans ce parking
    • unrecognized (valeur par défaut)
  • Géométrie : coordonnées du polygone défini, stockées dans une structure GeoJSON dans le champ displayBoundary. Ces coordonnées sont utilisées pour construire le contour de la section, du bâtiment ou du terrain.
  • Entrées : coordonnées de latitude et de longitude de toutes les entrées et sorties situées

La sélection de l'emplacement renvoie n'importe quel type composé associé à l'emplacement de recherche. Si le lieu de la recherche se trouve dans un magasin spécifique d'un centre commercial, l'option "Location Selection" renvoie : * le magasin spécifique, ses entrées et sorties, ainsi que le contour du magasin * le bâtiment complexe (le centre commercial), ses entrées et sorties, et les contours du centre commercial * le terrain complexe (centre commercial + parking), leurs entrées et sorties, et le contour de l'ensemble du terrain

Cette image montre les trois types composés renvoyés.

Cette image montre plusieurs lieux à l'intérieur d'un aéroport, avec les limites d'un bâtiment au sein de l'aéroport et les limites de l'aéroport et de tous ses terrains connexes.