【问题标题】:Google Maps API - Find Nearest LocationsGoogle Maps API - 查找最近的位置
【发布时间】:2016-06-08 08:16:17
【问题描述】:

我有一个包含美国各地大约 250 个办公地点的电子表格。我想要一个网页,用户可以在其中输入他们的家庭住址,并且五个最近的办公室(姓名、城市、电话号码等)将列在左侧,并将点映射在地图画布上。你知道我如何使用 Google Maps API 来实现这个场景吗?

我已经知道如何使用 GeoJSON 将数据加载到地图中,但我无法了解如何根据行驶距离或时间显示五个最近的站点。我确实找到了距离矩阵服务,但每个请求仅限于 25 个目的地,所以这不起作用。

其他人建议的半正弦法在这种情况下不起作用,因为我需要根据行驶距离或更有用的行驶时间进行计算。另外,在这种情况下我无法访问 PHP,但我们可以使用 JavaScript。

【问题讨论】:

    标签: google-maps google-maps-api-3


    【解决方案1】:

    使用harsine方法将点的数量减少到可以通过距离矩阵的数量,然后使用距离矩阵生成最后的5个点。

    Example

    代码 sn-p:

    var geocoder = null;
    var map = null;
    var customerMarker = null;
    var gmarkers = [];
    var closest = [];
    
    function initialize() {
      // alert("init");
      geocoder = new google.maps.Geocoder();
      map = new google.maps.Map(document.getElementById('map'), {
        zoom: 9,
        center: new google.maps.LatLng(52.6699927, -0.7274620),
        mapTypeId: google.maps.MapTypeId.ROADMAP
      });
      var infowindow = new google.maps.InfoWindow();
      var marker, i;
      var bounds = new google.maps.LatLngBounds();
      document.getElementById('info').innerHTML = "found " + locations.length + " locations<br>";
      for (i = 0; i < locations.length; i++) {
        var coordStr = locations[i][4];
        var coords = coordStr.split(",");
        var pt = new google.maps.LatLng(parseFloat(coords[0]), parseFloat(coords[1]));
        bounds.extend(pt);
        marker = new google.maps.Marker({
          position: pt,
          map: map,
          icon: locations[i][5],
          address: locations[i][2],
          title: locations[i][0],
          html: locations[i][0] + "<br>" + locations[i][2]
        });
        gmarkers.push(marker);
        google.maps.event.addListener(marker, 'click', (function(marker, i) {
            return function() {
              infowindow.setContent(marker.html);
              infowindow.open(map, marker);
            }
          })
          (marker, i));
      }
      map.fitBounds(bounds);
    
    }
    
    function codeAddress() {
      var numberOfResults = 25;
      var numberOfDrivingResults = 5;
      var address = document.getElementById('address').value;
      geocoder.geocode({
        'address': address
      }, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
          map.setCenter(results[0].geometry.location);
          if (customerMarker) customerMarker.setMap(null);
          customerMarker = new google.maps.Marker({
            map: map,
            position: results[0].geometry.location
          });
          closest = findClosestN(results[0].geometry.location, numberOfResults);
          // get driving distance
          closest = closest.splice(0, numberOfResults);
          calculateDistances(results[0].geometry.location, closest, numberOfDrivingResults);
        } else {
          alert('Geocode was not successful for the following reason: ' + status);
        }
      });
    }
    
    function findClosestN(pt, numberOfResults) {
      var closest = [];
      document.getElementById('info').innerHTML += "processing " + gmarkers.length + "<br>";
      for (var i = 0; i < gmarkers.length; i++) {
        gmarkers[i].distance = google.maps.geometry.spherical.computeDistanceBetween(pt, gmarkers[i].getPosition());
        document.getElementById('info').innerHTML += "process " + i + ":" + gmarkers[i].getPosition().toUrlValue(6) + ":" + gmarkers[i].distance.toFixed(2) + "<br>";
        gmarkers[i].setMap(null);
        closest.push(gmarkers[i]);
      }
      closest.sort(sortByDist);
      return closest;
    }
    
    function sortByDist(a, b) {
      return (a.distance - b.distance)
    }
    
    function calculateDistances(pt, closest, numberOfResults) {
      var service = new google.maps.DistanceMatrixService();
      var request = {
        origins: [pt],
        destinations: [],
        travelMode: google.maps.TravelMode.DRIVING,
        unitSystem: google.maps.UnitSystem.METRIC,
        avoidHighways: false,
        avoidTolls: false
      };
      for (var i = 0; i < closest.length; i++) {
        request.destinations.push(closest[i].getPosition());
      }
      service.getDistanceMatrix(request, function(response, status) {
        if (status != google.maps.DistanceMatrixStatus.OK) {
          alert('Error was: ' + status);
        } else {
          var origins = response.originAddresses;
          var destinations = response.destinationAddresses;
          var outputDiv = document.getElementById('side_bar');
          outputDiv.innerHTML = '';
    
          var results = response.rows[0].elements;
          // save title and address in record for sorting
          for (var i = 0; i < closest.length; i++) {
            results[i].title = closest[i].title;
            results[i].address = closest[i].address;
            results[i].idx_closestMark = i;
          }
          results.sort(sortByDistDM);
          for (var i = 0;
            ((i < numberOfResults) && (i < closest.length)); i++) {
            closest[i].setMap(map);
            outputDiv.innerHTML += "<a href='javascript:google.maps.event.trigger(closest[" + results[i].idx_closestMark + "],\"click\");'>" + results[i].title + '</a><br>' + results[i].address + "<br>" + results[i].distance.text + ' appoximately ' + results[i].duration.text + '<br><hr>';
          }
        }
      });
    }
    
    function sortByDistDM(a, b) {
      return (a.distance.value - b.distance.value)
    }
    
    google.maps.event.addDomListener(window, 'load', initialize);
    // Store Name[0],delivery[1],Address[2],Delivery Zone[3],Coordinates[4] data from FusionTables pizza stores example
    var locations = [
      ["John's Pizza", "no", "400 University Ave, Palo Alto, CA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.121277,37.386799,0 -122.158012,37.4168,0 -122.158012,37.448151,0 -122.142906,37.456055,0 -122.118874,37.45224,0 -122.107544,37.437793,0 -122.102737,37.422526,0 -122.113037,37.414618,0 -122.121277,37.386799,0   </coordinates></LinearRing></outerBoundaryIs></Polygon>", "37.447038,-122.160575", "http://maps.google.com/mapfiles/ms/icons/blue.png"],
      ["JJs Express", "yes", "1000 Santa Cruz Ave, Menlo Park, CA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.200928,37.438611,0 -122.158012,37.4168,0 -122.158012,37.448151,0 -122.142906,37.456055,0 -122.144623,37.475948,0 -122.164192,37.481125,0 -122.189255,37.478673,0 -122.208481,37.468319,0 -122.201271,37.438338,0 </coordinates></LinearRing></outerBoundaryIs></Polygon>", "37.448638,-122.187176", "http://maps.google.com/mapfiles/ms/icons/green.png"],
      ["John Paul's Pizzeria", "no", "1100 El Camino Real, Belmont, CA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.304268,37.516534,0 -122.300835,37.505096,0 -122.262383,37.481669,0 -122.242813,37.502917,0 -122.244186,37.534232,0 -122.269249,37.550021,0 -122.291222,37.545122,0 -122.302895,37.537499,0 -122.304268,37.516534,0 </coordinates></LinearRing></outerBoundaryIs></Polygon>", "37.520436,-122.275978", "http://maps.google.com/mapfiles/ms/icons/red.png"],
      ["JJs Express", "yes", "300 E 4th Ave, San Mateo, CA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.304268,37.516534,0 -122.348557,37.538044,0 -122.359886,37.56363,0 -122.364006,37.582405,0 -122.33654,37.589207,0 -122.281609,37.570433,0 -122.291222,37.545122,0 -122.302895,37.537499,0 -122.304268,37.516534,0 </coordinates></LinearRing></outerBoundaryIs></Polygon>", "37.564435,-122.322080", "http://maps.google.com/mapfiles/ms/icons/green.png"],
      ["John's Pizza", "yes", "1400 Broadway Ave, Burlingame, CA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.374306,37.548933,0 -122.348557,37.538044,0 -122.359886,37.56363,0 -122.364006,37.582405,0 -122.33654,37.589207,0 -122.359543,37.59764,0 -122.372246,37.604712,0 -122.417564,37.594648,0 -122.374306,37.548933,0 </coordinates></LinearRing></outerBoundaryIs></Polygon>", "37.584935,-122.366182", "http://maps.google.com/mapfiles/ms/icons/blue.png"],
      ["JJs Express", "yes", "700 San Bruno Ave, San Bruno, CA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.462883,37.628916,0 -122.445374,37.639247,0 -122.426147,37.648762,0 -122.405205,37.642238,0 -122.400055,37.628644,0 -122.392159,37.610696,0 -122.372246,37.604712,0 -122.417564,37.594648,0 -122.462196,37.628644,0  </coordinates></LinearRing></outerBoundaryIs></Polygon>", "37.630934,-122.406883", "http://maps.google.com/mapfiles/ms/icons/green.png"],
      ["JJs Express", "yes", "300 Beach St, San Francisco, CA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.43576,37.790795,0 -122.449493,37.801646,0 -122.425461,37.809784,0 -122.402115,37.811411,0 -122.390442,37.794593,0 -122.408295,37.79188,0 -122.434387,37.789167,0 </coordinates></LinearRing></outerBoundaryIs></Polygon>", "37.807628,-122.413782", "http://maps.google.com/mapfiles/ms/icons/green.png"],
      ["JJs Express", "yes", "1400 Haight St, San Francisco, CA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.463398,37.760266,0 -122.477349,37.774785,0 -122.427349,37.774785,0 -122.429237,37.763658,0 -122.46357,37.760808,0</coordinates></LinearRing></outerBoundaryIs></Polygon>", "37.770129,-122.445082", "http://maps.google.com/mapfiles/ms/icons/green.png"],
      ["JJs Express", "yes", "2400 Mission St, San Francisco, CA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.418766,37.747779,0 -122.425289,37.768951,0 -122.406063,37.769901,0 -122.406063,37.749679,0 -122.418251,37.747508,0 </coordinates></LinearRing></outerBoundaryIs></Polygon>", "37.758630,-122.419082", "http://maps.google.com/mapfiles/ms/icons/green.png"],
      ["JJs Express", "yes", "500 Castro St, Mountain View, CA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.121277,37.386799,0 -122.108917,37.362244,0 -122.077675,37.3385,0 -122.064285,37.378615,0 -122.069778,37.3898,0 -122.076645,37.402619,0 -122.078705,37.411619,0 -122.113037,37.414618,0 -122.121277,37.386799,0  </coordinates></LinearRing></outerBoundaryIs></Polygon>", "37.390040,-122.081573", "http://maps.google.com/mapfiles/ms/icons/green.png"],
      ["John's Pizza", "no", "100 S Murphy Ave, Sunnyvale, CA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.047119,37.33113,0 -122.065315,37.332495,0 -122.077675,37.3385,0 -122.064285,37.378615,0 -122.036819,37.385162,0 -122.006607,37.382162,0 -122.00386,37.342048,0 -122.047119,37.331403,0  </coordinates></LinearRing></outerBoundaryIs></Polygon>", "37.377441,-122.030071", "http://maps.google.com/mapfiles/ms/icons/blue.png"],
      ["John's Pizza", "no", "1200 Curtner Ave, San Jose, CA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-121.935196,37.345051,0 -121.931076,37.294267,0 -121.871338,37.293721,0 -121.806793,37.293174,0 -121.798553,37.361426,0 -121.879578,37.36088,0 -121.934509,37.345597,0 -121.935196,37.345051,0 </coordinates></LinearRing></outerBoundaryIs></Polygon>", "37.288742,-121.890765", "http://maps.google.com/mapfiles/ms/icons/blue.png"],
      ["John's Pizza", "yes", "700 Blossom Hill Rd, San Jose, CA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-121.935883,37.253287,0 -121.931076,37.294267,0 -121.871338,37.293721,0 -121.806793,37.293174,0 -121.790657,37.234702,0 -121.852455,37.223221,0 -121.935539,37.253014,0 </coordinates></LinearRing></outerBoundaryIs></Polygon>", "37.250543,-121.846563", "http://maps.google.com/mapfiles/ms/icons/blue.png"],
      ["John's Pizza", "yes", "100 N Milpitas Blvd, Milpitas, CA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-121.947556,37.435612,0 -121.934509,37.476493,0 -121.893311,37.469409,0 -121.852798,37.429615,0 -121.843872,37.400165,0 -121.887817,37.3898,0 -121.959915,37.420345,0 -121.959915,37.427979,0 -121.948929,37.435612,0 -121.947556,37.435612,0</coordinates></LinearRing></outerBoundaryIs></Polygon>", "37.434113,-121.901139", "http://maps.google.com/mapfiles/ms/icons/blue.png"],
      ["John's Pizza", "yes", "3300 Mowry Blvd, Fremont, CA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.02343,37.52198,0 -122.023773,37.558731,0 -121.989784,37.573426,0 -121.959572,37.566351,0 -121.944466,37.544305,0 -121.967125,37.520891,0 -122.023087,37.522525,0</coordinates></LinearRing></outerBoundaryIs></Polygon>", "37.552773,-121.985153", "http://maps.google.com/mapfiles/ms/icons/blue.png"],
      //New York, NY, USA (40.7127837, -74.00594130000002)  
      ["New York, NY, USA", "no", "New York City Hall, New York, NY 10007, USA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.02343,37.52198,0 -122.023773,37.558731,0 -121.989784,37.573426,0 -121.959572,37.566351,0 -121.944466,37.544305,0 -121.967125,37.520891,0 -122.023087,37.522525,0</coordinates></LinearRing></outerBoundaryIs></Polygon>", "40.7127837, -74.0059413", "http://maps.google.com/mapfiles/ms/icons/yellow.png"],
      // Newark, NJ, USA (40.735657, -74.1723667)  
      ["Newark, NJ, USA", "no", "169 Market St, Newark, NJ 07102, USA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.02343,37.52198,0 -122.023773,37.558731,0 -121.989784,37.573426,0 -121.959572,37.566351,0 -121.944466,37.544305,0 -121.967125,37.520891,0 -122.023087,37.522525,0</coordinates></LinearRing></outerBoundaryIs></Polygon>", "40.735657, -74.1723667", "http://maps.google.com/mapfiles/ms/icons/yellow.png"],
      // Baltimore, MD, USA (39.2903848, -76.6121893
      ["Baltimore, MD, USA", "no", "201-211 E Fayette St, Baltimore, MD 21202, USA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.02343,37.52198,0 -122.023773,37.558731,0 -121.989784,37.573426,0 -121.959572,37.566351,0 -121.944466,37.544305,0 -121.967125,37.520891,0 -122.023087,37.522525,0</coordinates></LinearRing></outerBoundaryIs></Polygon>", "39.2903848, -76.6121893", "http://maps.google.com/mapfiles/ms/icons/yellow.png"],
      // Boston, MA, USA (42.3600825, -71.05888
      ["Boston, MA, USA", "no", "City Hall Plaza, Boston, MA 02203, USA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.02343,37.52198,0 -122.023773,37.558731,0 -121.989784,37.573426,0 -121.959572,37.566351,0 -121.944466,37.544305,0 -121.967125,37.520891,0 -122.023087,37.522525,0</coordinates></LinearRing></outerBoundaryIs></Polygon>", "42.3600825, -71.05888", "http://maps.google.com/mapfiles/ms/icons/yellow.png"],
      // Philadelphia, PA, USA (39.9525839, -75.16522150000003)
      ["Philadelphia, PA, USA", "no", "1414 PA-611, Philadelphia, PA 19102, USA", "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.02343,37.52198,0 -122.023773,37.558731,0 -121.989784,37.573426,0 -121.959572,37.566351,0 -121.944466,37.544305,0 -121.967125,37.520891,0 -122.023087,37.522525,0</coordinates></LinearRing></outerBoundaryIs></Polygon>", "39.9525839, -75.1652215", "http://maps.google.com/mapfiles/ms/icons/yellow.png"]
    ];
    html,
    body,
    #map_canvas {
      margin: 0;
      padding: 0;
      height: 100%;
    }
    <script src="https://maps.googleapis.com/maps/api/js?libraries=geometry&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
    <table border="1">
      <tr>
        <td>
          <div id="map" style="height: 600px; width:500px;"></div>
        </td>
        <td>
          <div id="side_bar"></div>
        </td>
      </tr>
    </table>
    <input id="address" type="text" value="Palo Alto, CA"></input>
    <input type="button" value="Search" onclick="codeAddress();"></input>
    <div id="info"></div>

    【讨论】:

      【解决方案2】:

      /**

      • 使用此函数一次将 25 个目的地批量发送到 Google 距离矩阵函数,将结果返回到 X 个数组中,最后将它们全部合并。有关详细信息,请参阅 cmets。 .
      • 使用距离矩阵 API 计算从原点到每个商店的距离。
      • @param {google.maps.Data} data 地图的地理空间数据对象图层
      • @param {google.maps.LatLng} origin 纬度地理坐标
      • 和经度
      • @return {Promise} n Promise 由对象数组实现
      • 一个 distanceText、distanceVal 和 storeid 属性,按升序排序
      • 按距离值。 */
      async function calculateDistances(data, origin) {
      
          const destinations = [];
          const stores = [];
          const destinations_p = [];
      
          // Build parallel arrays for the store IDs and destinations
          data.forEach((store) => {
      
              const storeNum = store.getProperty('storeid');
              const storeLoc = store.getGeometry().get();
      
              destinations_p.push(storeLoc);
              stores.push(storeNum);
      
      
          });
      
          destinations.push(destinations_p, stores);
          //console.log(destinations);
          /******************************************************************************************
          I thought this step above would be helpful to make 2 dimentional array and send it to 
          distance metrix service and get storeid sorted in that function but distance matrix only 
          allow a set of inputs to be taken in consideration , we cannot set additional custom identtifier
      *******************************************************************************************/
      
      
      
          // Retrieve the distances of each store from the origin
          // The returned list will be in the same order as the destinations list
          const service = new google.maps.DistanceMatrixService();
          // console.log(service);
          const getDistanceMatrix =
              (service, parameters) => new Promise((resolve, reject) => {
                  service.getDistanceMatrix(parameters, (response, status) => {
                      if (status != google.maps.DistanceMatrixStatus.OK) {
                          reject(response);
                      } else {
                          const distances = [];
                          const results = response.rows[0].elements;
                          //console.log(response);
                          for (let j = 0; j < results.length; j++) {
                              const element = results[j];
                              //console.log(element);
                              const distanceText = (element.status != 'ZERO_RESULTS') ? element.distance.text : '9999999999 km';
                              const distanceVal = (element.status != 'ZERO_RESULTS') ? element.distance.value : '9999999999 km';
                              const distanceObject = {
                                  storeid: stores[j],
                                  distanceText: distanceText,
                                  distanceVal: distanceVal,
                              };
                              distances.push(distanceObject);
                          }
      
                          resolve(distances);
                      }
                  });
              });
      
      
          /***********************************************************************************
          This method was build to pass only 25 destinations in array to distance matrix 
          service , that's the max it was take at a time , if there is change to this 
          settings in google then only chnage max_records value below and it should work
          ************************************************************************************/
          let p_results = [];
          let p_process = [];
          let nextSet = [];
          var lastIndex = 0
          var max_records = 25;
          var p_stores = [];
          var flatArray = [];
          var total_records = Math.ceil(destinations[0].length / max_records);
      
      
          // later i figured out that foreach does not work with async function however FOR loop works
      
          /*  data.forEach((store) => {
            let p_val = store.getGeometry().get().lat()+","+store.getGeometry().get().lng();
            p_results.push("["+p_val+"]");
              });
          */
      
      
          for (var g = 1; g <= total_records; g++) {
      
              let nextSet = [];
      
              /*************************************************************
              These debugging could be use to match what we are sending 
              matching with destination and storeid'd in parallel array
              console.log(destinations[0].slice(lastIndex, max_records));
              console.log(destinations[1].slice(lastIndex, max_records));
              *************************************************************/
              nextSet.push(destinations[0].slice(lastIndex, max_records));
      
              lastIndex = max_records;
              max_records = lastIndex + 25;
      
              /*********************************************************************************************
              There is some issue going on with hawaii lats and lng - This is Test for that these are hawaii
              lat,lng , I have opened Ticket with google : https://issuetracker.google.com/u/2/issues/189164319
                 
                 p_process.push(await getDistanceMatrix(service, {
                    origins: ['21.304247103770592,-157.8504303328067'],
                    destinations: ['19.638878300603324,-155.9901696156307','19.666046167473695,-155.99401485525755','19.517828633585925,-155.92144357145344','19.927879613527526,-155.78726601617393','21.304247103770592,-157.8504303328067'],
                    travelMode: 'DRIVING',
                    unitSystem: google.maps.UnitSystem.METRIC,
                  }));
              
              *********************************************************************************************/
      
              p_process.push(await getDistanceMatrix(service, {
                  origins: [origin],
                  destinations: nextSet[0],
                  travelMode: 'DRIVING',
                  unitSystem: google.maps.UnitSystem.METRIC,
              }));
      
      
          }; // end for loop
      
      
          /**********************************************************
          So i get back array results from getDistance matrix and now
          they are nice stacked in 3,4,5 etc # of array depends on how
          many set of 25's where passed , now to process further more
          we need to merge all these array togather and stamp back 
          storeID - if you get 'undefined' error that means somehow 
          there is mismatch between array and loop count .
          **********************************************************/
      
      
      
          flatArray = Array.prototype.concat(...p_process);
          for (var h = 0; h < flatArray.length; h++) {
      
              flatArray[h].storeid = h; // reassigning storeid
      
          };
      
      
          // finally sorting array's
          flatArray.sort((first, second) => {
              return first.distanceVal - second.distanceVal;
          });
      
      
          return flatArray;
      
      }; // end function
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-05-06
        • 1970-01-01
        • 1970-01-01
        • 2014-07-01
        • 2014-03-05
        • 2011-05-02
        • 1970-01-01
        相关资源
        最近更新 更多