Как сделать форму, которая ищет элемент вокруг определенного радиуса с помощью Google maps API?

Я работаю над сайт в котором я хочу сделать круг на карте google либо вокруг текущего местоположения или некоторого ручного адреса.

  • пользователи будут иметь возможность решить, хотят ли они сделать круг вокруг текущего местоположения или какой-то случайный адрес, который они предоставят. (пользователи будут иметь возможность поместить ручной адрес внутри текущего местоположения, как показано ниже на рисунке)

  • теперь мы должны убедитесь, что круг имеет определенный радиус (0-20 / 70 км от текущего местоположения), а также и пользователь должен решить, что хорошо. (линия под текущим местоположением решит радиус, который пользователи могут перемещать здесь и там 0-70km)

например: пользователь хочет создать круг из текущего местоположения до 30 км или пользователь хочет создать круг из некоторого случайного адреса до 20км.

enter image description here

HTML-код, который я использовал, чтобы сделать строка поиска для радиуса поиска:

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



Постановка Задачи:

(1) мне интересно, какие изменения мне нужно сделать или код мне нужно добавить так, чтобы элементы искали вокруг определенного радиуса. Я думаю, мне нужно интегрировать код Google Maps круг но я не уверен, как я могу сделать это.

(2) при попадании радиуса поиска на сайт в нижней части появятся следующие параметры/экран:

enter image description here

2 ответов


давайте попробуем дать вам несколько первых шагов, я бы не код всего приложения, а скорее дать вам некоторые направляющие линии в том, как решить небольшие подзадачи, которые у вас есть:

добавление круга в карту

Ну, для этого у вас есть множество различных вариантов, но наиболее важной частью является функция 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
    });
}

центр может исходить от клик например:

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

или положение определенного адреса (это также задокументировано), или любой другой метод (перетащите круг, перетащите маркер blablabla)

добавление динамического радиуса

Ну, если мы знаем, что радиус круга задается в метрах, тогда очень легко дать функции addCircle правильный радиус. Например, 20км - > 20 000 метров. Таким образом, вы просто должны иметь доступ к radius при вызове addCircle (это может быть аргумент, глобальная переменная... ваш выбор.)

мы закончили с чертежной частью, теперь давайте искать внутри этого круга.

получение только маркеров внутри круга

здесь есть необходимое условие, чтобы иметь все маркеры вашей карты. У вас может быть массив мест, которые вы получаете из базы данных, или, возможно, вы получить маркеры из 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;

остальная часть работы должна быть такой же простой,поместите маркеры на карте и делайте с этой информацией все, что хотите.

дополнительно

в качестве дополнительной помощи есть пара примеры/ответы, которые могут быть полезны для вас:

полный пример API Google Map, очень простое пошаговое руководство.

Radius поиск с помощью мест, хороший ответ в том, как сделать поиск радиуса.

пример поиска radius, откройте F12 и отладьте код, если хотите, но его легко следовать.

EDIT**: я не понял, что 2 из этих ссылок, где также указано в комментариях.


Я рекомендую использовать рабочий поток для поиска, чтобы освободить поток пользовательского интерфейса, выполнив поиск в фоновом режиме. Это также полезно, если пользователь перемещает круг или расширяет/сжимает его, поскольку предыдущий поиск / рендеринг соответствующих маркеров может быть оставлен,

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

упомянутые функции" Toolbox " являются следующими: -

    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

Ура Ричард