如何使用谷歌地图API制作一个搜索特定半径周围项目的表单?

Posted

技术标签:

【中文标题】如何使用谷歌地图API制作一个搜索特定半径周围项目的表单?【英文标题】:How to make a form which searches an item around a specific radius using google maps API? 【发布时间】:2019-01-04 18:08:03 【问题描述】:

我正在处理website,我想在谷歌地图上围绕当前位置或一些手动地址制作一个圆圈。

用户可以选择决定是要绕当前位置还是要提供的随机地址。 (用户可以选择将手动地址放入当前位置,如下图所示)

现在我们还需要确保圆具有特定的半径(距当前位置 0-20/70 公里),并且用户也需要做出决定。 (当前位置下方的线将决定用户可以在这里和那里移动0-70公里的半径

例如:用户想要创建一个从当前位置到 30KM 的圈子,或者用户想要从某个随机地址创建一个圈子直到 20KM。

我用来制作搜索半径搜索栏的 HTML 代码是

<div class="input-searchradius">
   <input class="form-control search_radius mb-4" type="text" placeholder="search radius">
</div>

问题陈述:

(1) 我想知道我需要进行哪些更改或需要添加代码,以便围绕特定半径搜索项目。我想,我需要整合代码Google Maps circle,但我不确定我该怎么做。

(2) 在website 上点击搜索半径时,底部会出现以下选项/屏幕:

【问题讨论】:

嗯,我没有收到弹出窗口。但我想我帮不了你,它就像你想要构建的Google Maps API,但我对半径等不太熟悉。 1. 假设我在what are you looking for.. 占位符中寻找计算器2. 点击search radius 占位符,它将询问距我们当前位置(或我们手动放置的地址)大约 0-70 公里的地理位置(要搜索的项目,比如计算器)。现在忘记弹出窗口。让我们专注于功能。 @user5447339 检查Google Map: Drawing Tool 或Google Map: circle。用户填写完表格(位置、半径)后,在gmap的位置放一个标记,然后根据半径值画一个圆。 @Sphinx 你能给我指点如何处理这个问题吗?我以前从未使用过 google api。 我不熟悉 Google MAP API。您可以按照谷歌地图的指南首先达到您的目标。如果有的话,*** 将是您的家。 :) 【参考方案1】:

让我们尝试为您提供一些初步步骤,我不会编写整个应用程序的代码,而是为您提供一些指导方针,帮助您解决您遇到的小子问题:

在地图中添加圆圈

好吧,为此你有许多不同的输入选项,但最重要的部分是 addCircle 函数:

function addCircle(center)
    circle = new google.maps.Circle(
        map: map, //The existing map
        center: center,
        radius: 200, //This will be modified afterwards
        zindex: 100
    );

中心可能来自click,例如:

// Area is wherever you want to attach the click, either a polygon, a map...
google.maps.event.addListener(area, "click", function(event) 
        addCircle(event.latLng);
);

或通过获取某个地址的位置 (This is documented as well),或任何方法(拖放圆圈,拖动标记 blablabla)

添加动态半径

好吧,如果我们知道the radius of the Circle is given in meters,那么就很容易给 addCircle 函数一个正确的半径。例如,20 公里 -> 20 000 米。所以你只需要在调用 addCircle 时能够访问半径(它可以是一个参数,一个全局变量......你的选择)。

我们已经完成了绘图部分,现在让我们在那个圆圈内搜索。

仅获取圆圈内的标记

这里有一个先决条件,即拥有地图的所有标记。您可能有一系列从数据库中获取的地点,或者您可能是 get the markers from Google Maps API(例如地点搜索)。

之后,您必须计算这些标记与给定中心之间的距离,并检查距离是否小于您的半径(使用computeDistanceBetween 非常简单),这样您就会知道哪些标记对您有效.

const markers = [//array of my valid markers with position];
markers.filter( (marker) => 
    google.maps.geometry.spherical.computeDistanceBetween(marker.getPosition(), center.getPosition()) < radius; // Filter the markers which distance is bigger than radius;

其余的工作应该很简单,place the markers in the map 并根据这些信息做任何你想做的事情。

EXTRAS

作为进一步的帮助,有一对示例/答案可能对您有用:

Full Google Map API example,非常简单的分步指南。

Radius search using places,关于如何进行半径搜索的一个很好的答案。

Example of radius search,喜欢的话可以打开F12调试代码,不过很容易上手。

编辑**:我没有意识到这些链接中的 2 个也在 cmets 中指出。

【讨论】:

谢谢@SirPeople 我有一个类似的question。我想知道你是否可以看看。 给我几分钟/几小时,我会看看 :)【参考方案2】:

我建议使用工作线程进行搜索,通过在后台进行搜索来释放 UI 线程。如果用户移动圆圈或扩大/缩小圆圈,这也很有用,因为可以放弃之前搜索/呈现匹配标记,

importScripts("Tier3Toolbox.js");

var currVintage = 0;
var inBounds = false;
var facFilter = [];
var imageProlog = "<div style='height:5em; width:5em; display:inline-block;vertical-align:middle;'>" +
                  "<img style='height:100%; width: 100%; max-height:100%; max-width:100%' src='";
var imageEpilog = "' ></div>";
var facilityTable, lineBreak;

self.addEventListener('message', function(e) 

  var data = e.data;
  switch (data.cmd) 
    case 'init':
      initThread(data.load);
      break;
    case 'initFilter':
      for (var i=0; i<data.filterTable.length; i++) 
        facFilter[data.filterTable[i].locTypeId] = 'icon':data.filterTable[i].icon;
      
      break;
    case 'filter':
      facFilter = [];
      for (var i=0; i<data.filterTable.length; i++) 
        if (data.filterTable[i].facSelected)
          facFilter[data.filterTable[i].locTypeId] = 'icon':data.filterTable[i].icon;
      
      break;
    case 'search':
      var searchVintage = ++currVintage;
      var tableSearch = new searcher(searchVintage, data);
      break;
    case 'reset':
      reset();
      self.postMessage('reset': true);
      break;
    case 'stop':
      self.postMessage('success' : true);
      self.close(); 
      break;
    default:
      self.postMessage('success' : false, 'msg' : data.msg);
  ;
, false);

function initThread(msg) 

    facilityTable = JSON.parse(msg);
    reset();

    self.postMessage('success' : true, 
                      'cnt'     : facilityTable.length
                    );     
   

function reset() 

    for (var i=0; i<facilityTable.length; i++) 
        facilityTable[i].visible=false
    
    currVintage = 0;
   

function searcher(searchVintage, msg)

    var myVintage = searchVintage;
    var facIndex  = -1;
    var msg       = msg;

    var checkLoop = function()
    
        if (myVintage != currVintage)
            return;

        if (++facIndex == facilityTable.length)
            return;

        inBounds = geoFencer.call(this, msg);

        if (inBounds) 
            var facMatch = 0;
            var bubblehtml = "";
            for (var i=0; i<facilityTable[facIndex].facilities.length; i++)
                var currFac = facilityTable[facIndex].facilities[i];
                if (facFilter[currFac.locTypeId] != undefined) 
                    if (facMatch != 0) 
                        lineBreak = (facMatch / 3);
                        if (lineBreak == lineBreak.toFixed(0)) 
                            bubbleHTML += "<br />";
                        
                    
                    facMatch++;
                    bubbleHTML += imageProlog + facFilter[currFac.locTypeId].icon + imageEpilog;

                
            
            if (facMatch == 0) 
                inBounds = false;
            
        

        if (inBounds != facilityTable[facIndex].visible) 
            self.postMessage('match'       : inBounds,
                              'facIndex'    : facIndex,
                              'scopeVintage': msg.scopeVintage,
                              'bubbleHTML'  : bubbleHTML,
                              'success'     : true
                            ); 
            facilityTable[facIndex].visible = inBounds;
        

        setTimeout(checkLoop,0);
    

    var circleCheck = function(msg) 
    
        var diff = Tier3Toolbox.calculateDistance(
                        msg.centerLat,
                        msg.centerLng,
                        facilityTable[facIndex].searchLat,
                        facilityTable[facIndex].searchLng);

        if (msg.radius > diff)
            return true;        

        return false;
    

    var rectangleCheck = function(msg) 
    
        if (facilityTable[facIndex].searchLat > msg.SWLat &&
            facilityTable[facIndex].searchLat < msg.NELat &&
            facilityTable[facIndex].searchLng > msg.SWLng &&
            facilityTable[facIndex].searchLng < msg.NELng)
            return true;        

        return false;
    

    var GEOFENCER = [circleCheck,rectangleCheck];
    var geoFencer = GEOFENCER[msg.checker];

    setTimeout(checkLoop,0);
    return this;

所指的“工具箱”功能是:-

    function Tier3Toolbox() 

    return this;


Tier3Toolbox.EARTH_RADIUS = 6378137; /* Equitorial Radius instead of 6371000 */
Tier3Toolbox.toRad = 
    function (num) 
        return num * Math.PI / 180;
    ;  
Tier3Toolbox.calculateDistance =
    function(lat1, lon1, lat2, lon2)
        var dLat = this.toRad(lat2 - lat1);
        var dLon = this.toRad(lon2 - lon1);
        var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(this.toRad(lat1)) * 
                Math.cos(this.toRad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
        var distance = this.EARTH_RADIUS * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return distance;
    

Tier3Toolbox.prototype.callAJAX = 
    function(url, method, callback, serverArgs) 
    
        var callback = callback;
        var xmlhttp;
        var target = url;
        var args = (serverArgs != undefined) ? serverArgs : "";
        var postArgs = "";
        var callbackArgs = new Array();

        for (i = 4; i < arguments.length; i++) 
            callbackArgs[i - 3] = arguments[i];
        

        if (window.XMLHttpRequest) 
            xmlhttp = new XMLHttpRequest();
         else 
            xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        

        callbackArgs[0] = xmlhttp;

        if (method.toUpperCase() == "GET") 
            target = target + "?" + args;
         

        xmlhttp.onreadystatechange = function () 
            if (xmlhttp.readyState == 4) 
                if (xmlhttp.status == 200) 
                    callback.apply(this, callbackArgs)
                 else 
                    throw new Error("Error making Ajax call to " + target + " Status = " + xmlhttp.status);
                
            
        ;

        xmlhttp.open(method, url, true);
        if (method.toUpperCase() == "POST") 
            xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            postArgs = args;
        
        xmlhttp.send(postArgs);
    

Tier3Toolbox.reportError =      
    function(error) 
    
        var header  = error.header  || "Error";
        var message = error.message || "";
        var topWindow=window.top.document.open();
        topWindow.write("<!DOCTYPE html><html><body style='height: 100%;'><hr><h1>" + header + "</h1><hr>");
        topWindow.write("<h2>Please contact Server Support for assistance.</h2><br />");
        topWindow.write('<p style="color:red">' + message + "</p></body></html>");
        topWindow.close();
        return;
    

在您的主线中,您需要添加以下侦听器:-

    google.maps.event.addDomListener(radarCircle,    'center_changed', reScope);
    google.maps.event.addDomListener(radarCircle,    'radius_changed', reScope);
    google.maps.event.addDomListener(radarRectangle, 'bounds_changed', reScope);


    function createFacilityMarkers(xmlhttp)
        facFinder = new Worker("facfinder.js");
        facFinder.addEventListener('message', workerInit, false);

        facFinder.postMessage('cmd' : 'init', 'load' : xmlhttp.responseText);
     

    function reScope() 

    var searchReq = 'cmd':'search', 'scopeVintage':scopeVintage;
    if (radarShape.getCenter) 
        searchReq.checker = 0;
        var currCenter = radarCircle.getCenter();
        searchReq.centerLat = currCenter.lat();
        searchReq.centerLng = currCenter.lng();         
        searchReq.radius = radarCircle.getRadius();
     else 
        searchReq.checker = 1;
        searchReq.SWLat = radarShape.getBounds().getSouthWest().lat();
        searchReq.SWLng = radarShape.getBounds().getSouthWest().lng();
        searchReq.NELat = radarShape.getBounds().getNorthEast().lat();
        searchReq.NELng = radarShape.getBounds().getNorthEast().lng();
    

    facFinder.postMessage(searchReq);

HTH

干杯理查德

【讨论】:

谢谢@McMurphy 我有一个类似的question 我想知道你是否可以看看。【参考方案3】:

这里遵循@SirPeople 的建议是解决您从用户获取位置输入、更新地图并设置其周围的动态半径的核心问题陈述的完整代码。

这里是 JS Fiddle 链接https://jsfiddle.net/innamhunzai/63vcthp7/3/

JS:

var circle;
var map;
function initMap() 
    var centerCoordinates = new google.maps.LatLng(37.6, -95.665);
    map = new google.maps.Map(document.getElementById('map'), 
              center: centerCoordinates,
              zoom: 4
          );
    var card = document.getElementById('pac-card');
    var input = document.getElementById('pac-input');
        
    var infowindowContent = document.getElementById('infowindow-content');
        
    var autocomplete = new google.maps.places.Autocomplete(input);
    var infowindow = new google.maps.InfoWindow();
    infowindow.setContent(infowindowContent);
        
    var marker = new google.maps.Marker(
          map: map
    );
        
     
        circle = new google.maps.Circle(
           map: map,
           strokeColor: "#FF0000",
           strokeOpacity: 0.8,
           strokeWeight: 2,
           fillColor: "#FF0000",
           fillOpacity: 0.35,
        );

    autocomplete.addListener('place_changed', function() 
            document.getElementById("location-error").style.display = 'none';
            infowindow.close();
            marker.setVisible(false);
                var place = autocomplete.getPlace();
                if (!place.geometry) 
                    document.getElementById("location-error").style.display = 'inline-block';
                    document.getElementById("location-error").innerHTML = "Cannot Locate '" + input.value + "' on map";
                    return;
                
                
            map.fitBounds(place.geometry.viewport);
            marker.setPosition(place.geometry.location);
            circle.setCenter(place.geometry.location);
                marker.setVisible(true);
            circle.setVisible(true);
                    
                infowindowContent.children['place-icon'].src = place.icon;
                infowindowContent.children['place-name'].textContent = place.name;
                infowindowContent.children['place-address'].textContent = input.value;
                infowindow.open(map, marker);
        );
    
    
    function updateRadius() 
    circle.setRadius(document.getElementById('radius').value * 1609.34);
        map.fitBounds(circle.getBounds());


**CSS:**
#map 
    height: 400px;
  

**HTML**
<html>
    <link href="style.css" rel="stylesheet" type="text/css">
  <body>
    <div class="pac-card" id="pac-card">
      <div>
        <div id="label">
          Location search
        </div>       
      </div>
      <div id="pac-container">
        <input id="pac-input" type="text" placeholder="Enter a location">
        <div id="location-error"></div>
      </div>
      <div>
        <input type="range" id="radius" name="radius" min="0" max="100" onchange="updateRadius()">
      </div>
    </div>
    <div id="map"></div>
    <div id="infowindow-content">
      <img src=""   id="place-icon">
      <span id="place-name"  class="title"></span><br>
      <span id="place-address"></span>
    </div>
    <script src="https://maps.googleapis.com/maps/api/js?libraries=places&callback=initMap"
        async defer></script>
    </body>
</html>

【讨论】:

以上是关于如何使用谷歌地图API制作一个搜索特定半径周围项目的表单?的主要内容,如果未能解决你的问题,请参考以下文章

点周围的半径也称为 ASP.NET C# 中的地理围栏与谷歌地图[关闭]

在谷歌地图中围绕一个点绘制半径

如何在谷歌地图中制作多个圆圈颤动[关闭]

如何使用 api 在谷歌地图中仅显示一个国家或特定区域?

WordPress ACF 谷歌地图半径搜索

如何在特定半径内的地图上显示点