Pesquisar pontos de embarque usando a seleção de local (Beta)

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


    

Antes de usar a API Location Selection para pesquisar pontos de embarque de viagens, siga as instruções para fazer a integração com a biblioteca de cliente.

O serviço Location Selection oferece três APIs para seleção de retirada e devolução: FindNearbyPlaces, FindPickupPointsForPlace e FindPickupPointsForLocation.

Use FindNearbyPlaces para buscar lugares perto da pesquisa ou da localização do dispositivo. Os lugares são classificados por proximidade ao local e destaque para compartilhamento de caronas. FindNearbyPlaces retorna uma lista de lugares que podem ser mostrados para que o usuário faça a melhor seleção. Depois que um lugar for selecionado, use FindPickupPointsForPlace para buscar pontos de retirada.

Use FindPickupPointsForLocation para buscar lugares e os pontos de retirada associados perto da pesquisa ou da localização do dispositivo na mesma chamada de RPC. Cada ponto de coleta está associado a um lugar. Os pontos de encontro são classificados por proximidade ao local e relevância para o compartilhamento de viagens. Os pontos de coleta para vários lugares são ordenados juntos. FindPickupPointsForLocation combina FindNearbyPlaces e FindPickupPointsForPlace. Por exemplo, digamos que o local da solicitação esteja perto dos lugares P1, P2 e P3. Se o melhor ponto de encontro T1 estiver associado ao lugar P2 e o próximo melhor ponto de encontro estiver associado ao lugar P1, os resultados terão a ordem [T1:P2, T2:P1, ...].

O ranking dos pontos de retirada depende dos critérios fornecidos na solicitação. Para mais informações, consulte Otimizar coletas para várias viagens.

Pesquisar por local

Se você preferir mostrar lugares ao usuário antes da seleção de pontos de retirada ou exibir lugares próximos arrastando um marcador, use a seguinte chamada de RPC:

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

A chamada RPC retorna uma lista classificada de respostas de lugares que atendem aos critérios de entrada, ordenadas por uma combinação de proximidade e destaque. Você pode deixar o passageiro escolher um lugar ou usar o primeiro resultado e prosseguir para a seleção do ponto de encontro. Cada resposta de lugar tem um place_id exclusivo que pode ser usado em FindPickupPointsForPlaceRequest para buscar pontos de retirada. Para mais informações, consulte Pesquisar por ID de lugar.

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

A chamada de RPC retorna uma lista classificada de pontos de retirada que atendem aos critérios de entrada, ordenada por uma combinação de proximidade e destaque. Essa chamada de RPC combina FindNearbyPlaces e FindPickupPointsForPlaceRequest e pode ser usada em vez de combinar as outras duas chamadas.

Pesquisar por ID do lugar

Você pode conseguir um place_id usando FindNearbyPlaces ou o serviço Autocomplete da API Places. Em seguida, use a seguinte chamada de RPC para fornecer os pontos de coleta ideais para o lugar especificado:

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 retorna PickupPointResponses com latitude e longitude dos pontos de embarque de um passageiro.

Otimizar coletas para várias viagens

Para viagens compartilhadas ou consecutivas, o FindPickupPointsForPlace permite ordenar os pontos de encontro por DRIVING_ETA_FROM_PICKUP_POINT_TO_DESTINATION. Assim, é possível retornar um ponto de encontro para a próxima viagem otimizada com o trajeto da viagem atual do motorista.

Exemplo

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

Mostrar contornos, entradas e saídas de edifícios

Na mensagem de resposta do proto de lugar, o campo associatedCompounds identifica os compostos associados ao lugar. A mensagem composta contém três tipos de informações:

  • Tipo composto: uma das quatro opções
    • compoundBuilding: um único edifício independente, como um shopping ou supermercado.
    • compoundSection: um complexo dentro de um complexo maior, como uma única loja em um shopping.
    • compoundGrounds: tudo associado a um compoundBuilding, como um shopping, o estacionamento dele e qualquer outro prédio dentro desse estacionamento.
    • unrecognized: o valor padrão.
  • Geometria: as coordenadas do polígono delineado, armazenadas em uma estrutura GeoJSON no campo displayBoundary. Essas coordenadas são usadas para construir o contorno da seção, do edifício ou do terreno.
  • Entradas: as coordenadas de latitude e longitude de todas as entradas e saídas localizadas.

A seleção de local retorna qualquer tipo composto associado ao local de pesquisa. Se o local de pesquisa for uma loja específica em um shopping, a seleção de local vai retornar: * a loja específica, as entradas e saídas dela e o contorno da loja * o edifício composto (o shopping), as entradas e saídas dele e o contorno do shopping * o terreno composto (o shopping + estacionamento), as entradas e saídas dele e o contorno de todo o terreno

Esta imagem mostra todos os três tipos compostos sendo retornados.

Esta imagem mostra vários locais dentro de um aeroporto, com o limite de um prédio dentro do aeroporto e o limite do aeroporto e de todas as áreas relacionadas.