简体   繁体   English

如何使用google maps API制作搜索特定半径周围项目的表单?

[英]How to make a form which searches an item around a specific radius using google maps API?

I am working on a website in which I want to make a circle on google map either around current location or some manual address. 我正在一个网站上工作,我想在Google地图上围绕当前位置或某个手动地址制作一个圆圈。

  • Users will have option to decide whether they want to make circle around current location or some random address they will provide. 用户可以选择是否要围绕当前位置或他们将提供的随机地址进行圈选。 (Users would have the option to put manual address inside current location as shown below in an image) (用户可以选择将手动地址放在当前位置,如下图所示)

  • Now we also need to make sure that circle is of particular radius ( 0-20/70km from the current location ) as well and user needs to decide that as well. 现在我们还需要确保圆圈具有特定的半径( 距当前位置0-20 / 70km ),用户也需要确定它。 ( The line beneath the current location will decide the radius which users can move here and there 0-70km ) 当前位置下方的线将决定用户可在此处移动的半径0-70km

For example: user want to create a circle from current location till 30KM or user want to create a circle from some random address till 20KM. 例如:用户想要从当前位置创建一个圆圈直到30KM,或者用户想要从一些随机地址创建一个圆圈直到20KM。

在此输入图像描述

The HTML code which I have used in order to make a search bar for search radius is : 我用来制作搜索范围搜索栏的HTML代码

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



Problem Statement: 问题陈述:

(1) I am wondering what changes I need to make or code I need to add so that the items are being searched around a specific radius. (1)我想知道我需要进行哪些更改或需要添加的代码,以便围绕特定半径搜索项目。 I think, I need to integrate the code Google Maps circle but I am not sure how I can do that. 我想,我需要整合代码谷歌地图圈子,但我不知道我该怎么做。

(2) On hit of search radius on the website the following options/screen will appear at the bottom: (2)网站上搜索半径命中时,底部会显示以下选项/屏幕:

在此输入图像描述

Let's try to give you some first steps, I would no code the whole app, but rather give you some guide lines in to how to solve the small subproblems that you have: 让我们尝试给你一些第一步,我不会编写整个应用程序的代码,而是给你一些如何解决你所拥有的小子问题的指导:

Adding a circle in a map 在地图中添加圆圈

Well, for this you have many different options for input, but the most important part is the addCircle function: 那么,为此您有许多不同的输入选项,但最重要的部分是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
    });
}

The center could come from a click for example: 该中心可能来自点击 ,例如:

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

OR by getting the position of a certain address ( This is documented as well ), OR whatever method (drag and drop the circle, drag the marker blablabla) 或者通过获取某个地址的位置( 这也记录在案 ),或者任何方法(拖放圆圈,拖动标记blablabla)

Adding a dynamic radius 添加动态半径

Well, if we know that the radius of the Circle is given in meters , then is very easy to give the addCircle function the correct radius. 好吧,如果我们知道圆的半径以米为单位 ,那么很容易给addCircle函数提供正确的半径。 For example, 20km -> 20 000 meters. 例如,20km - > 20 000米。 So you just have to be able to access radius when calling addCircle (it can be an argument, a global variable... your choice). 所以你必须能够在调用addCircle时访问radius(它可以是一个参数,一个全局变量......你的选择)。

We are done with the drawing part, lets now search within that circle. 我们完成了绘图部分,现在让我们在该圆圈内搜索。

Getting only the markers inside the circle 仅获取圆圈内的标记

There is a prerequisite here, to have all the markers of your map. 这里有一个先决条件,即拥有地图的所有标记。 You may have an array of places that you get from a database or maybe you get the markers from Google Maps API (Place search for example). 您可能拥有从数据库获得的一系列地点,或者您可能从Google Maps API获取标记 (例如,放置搜索)。

After that you will have to compute the distance between those markers and your given center, and check if the distance is smaller than your radius (with computeDistanceBetween is very easy), so you will know which markers are valid for you. 之后,您将必须计算这些标记与给定中心之间的距离,并检查距离是否小于您的半径(使用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;

The rest of the job should be as easy, place the markers in the map and do whatever you like with this information. 剩下的工作应该很简单, 将标记放在地图中 ,并使用这些信息做任何你喜欢的事情。

EXTRAS EXTRAS

As a further help there are a pair of examples/answers that may be useful for you: 作为进一步的帮助,有一对可能对您有用的示例/答案:

Full Google Map API example , very easy step by step guide. 完整的Google Map API示例 ,非常简单的一步一步指南。

Radius search using places , a good answer in to how to do radius search. 半径搜索使用的地方 ,如何做半径搜索的好答案。

Example of radius search , open F12 and debug the code if you like, but it is easy to follow. 半径搜索示例 ,打开F12并根据需要调试代码,但很容易遵循。

EDIT**: I did not realize that 2 of these link where also pointed out in the comments. 编辑**:我没有意识到这些链接中的2个也在评论中指出。

I recommend using a worker thread to do the searching to free up the UI thread by doing the searching in the background. 我建议使用工作线程进行搜索以通过在后台进行搜索来释放UI线程。 This is also useful if the user moves the circle or expands/contracts it as the previous search/render of matching markers can be abandoned, 如果用户移动圆圈或扩展/收缩圆圈,这也很有用,因为可以放弃先前的搜索/渲染匹配标记,

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

The "Toolbox" functions referred to are : - 提到的“工具箱”功能是: -

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

In you mainline you need to add listeners like: - 在你的主线你需要添加如下的听众: -

    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 HTH

Cheers Richard 干杯理查德

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM