Ищем ближайшие организации от указанного места и показываем их на Google Maps

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

В этой заметке я расскажу как это сделать, с использованием API Google Maps v3.

Для примера мы будем искать ближайшие кафе в Нижнем Новгорде.

Пользователь вводит свой адрес в форму над картой и радиус поиска в километрах, ему выдается найденный результат на карте, а слева от карты список кафе отсортированный по растоянию.

Посмотреть пример в действии.

В начале необходимо подготовить исходные данные о кафе.

Они взяты из открытых источников в интернете.

Затем, с помощью скрипта из заметки «Используем HTML-геокодер для Google Maps JavaScript API v3», были получены значения координат для собранных адресов и записаны в базу данных.

Как работает данный пример.

Введенное пользователем значение адреса, мы используя геокодер API Google Maps v3 преобразуем в пару координат — значения широрты и долготы, которые затем в месте с радиусом передаем скрипту phpsqlsearch_genjson.php.

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

Данные сортируются по расстоянию от центра окружности.

Вот код запроса:

$query = sprintf("SELECT address, name, rayon, lat, lng, ( 6371 * acos( cos( radians('%s') ) * cos( radians( lat ) ) * cos( radians( lng ) - radians('%s') ) + sin( radians('%s') ) * sin( radians( lat ) ) ) ) AS distance FROM markers_ussearch HAVING distance < '%s' ORDER BY distance LIMIT 0 , 50",
  mysql_real_escape_string($lat),
  mysql_real_escape_string($lng),
  mysql_real_escape_string($lat),
  mysql_real_escape_string($radius));

Из массива полученных данных формируется ответ в формате JSON, который отправляется на страницу с картой.

Полный код файла phpsqlsearch_genjson.php:

<?php  
header("Content-type: text/html;charset=UTF-8");
require("config.php");
 
$lat = $_GET["lat"];
 
$lng = $_GET["lng"];
 
$radius = $_GET["radius"];
 
$query = sprintf("SELECT address, name, rayon, lat, lng, ( 6371 * acos( cos( radians('%s') ) * cos( radians( lat ) ) * cos( radians( lng ) - radians('%s') ) + sin( radians('%s') ) * sin( radians( lat ) ) ) ) AS distance FROM markers_ussearch HAVING distance < '%s' ORDER BY distance LIMIT 0 , 50",
  mysql_real_escape_string($lat),
  mysql_real_escape_string($lng),
  mysql_real_escape_string($lat),
  mysql_real_escape_string($radius));
 
$result = mysql_query($query);
 
 
if (!$result) {
  die("Invalid query: " . mysql_error());
}
 
while ($par1 = mysql_fetch_array($result)){
 
$places[] = array("name"=>$par1['name'],
 
"address" => $par1['address'],
 
"rayon" => $par1['rayon'],
 
"lat" => $par1['lat'],
 
"lng" => $par1['lng']);
 
}
 
$json = json_encode($places);
 
echo $json;
 
?>

Файл config.php служит для соединения с базой данных и имеет следующий код:

<?php
 
$sdb_name = "localhost";
$user_name = "root";
$user_password = "";
$db_name = "gmap_bd";
 
// соединение с сервером базы данных
if(!$link = mysql_connect($sdb_name, $user_name, $user_password))
{
  echo "<br>Не могу соединиться с сервером базы данных<br>";
  exit();
}
 
// выбираем базу данных
if(!mysql_select_db($db_name, $link))
{
  echo "<br>Не могу выбрать базу данных<br>";
  exit();
}
 
mysql_query('SET NAMES utf8');
 
?>

Давайте теперь рассмотрим код основного файла usersearch_gmapv3_radius.html

<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>Поиск по своим данным от произвольной точки в пределах окружности заданного радиуса, с выводом на Google Maps</title> 
<script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
 
<script type="text/javascript"> 
var map, infoWindow, userMarker = [], side_bar_html = ""; 
 
var gmarkers = new Array();
 
var infowindow = new google.maps.InfoWindow(
  { 
    size: new google.maps.Size(150,50)
});
 
function createMarker(latlng, name, html) {
    var contentString = html;
    var marker = new google.maps.Marker({
        position: latlng,
        map: map        
        });
 
google.maps.event.addListener(marker, 'click', function() {
        infowindow.setContent(contentString); 
        infowindow.open(map,marker);
});
 
gmarkers.push(marker);
side_bar_html += '<a href="javascript:myclick(' + (gmarkers.length-1) + ')">' + name + '</a><br>';
}
 
function initialize() {
      map = new google.maps.Map(document.getElementById("map"), {
        center: new google.maps.LatLng(56.317213,43.993976),
        zoom: 12,
        mapTypeId: 'roadmap',
        mapTypeControlOptions: {style: google.maps.MapTypeControlStyle.DROPDOWN_MENU}
      }); 
 infoWindow = new google.maps.InfoWindow();
}
 
function searchLocations() {		
 
	 var address = $('#addressLoc').val();
	 var radius = $('#radiusSelect').val();	 
 
	var geocoder = new google.maps.Geocoder();
     geocoder.geocode({address: address}, function(results, status) {
       if (status == google.maps.GeocoderStatus.OK) {
        var locations_lat = results[0].geometry.location.lat();
		var locations_lng = results[0].geometry.location.lng();
 
		while(userMarker[0]){
			userMarker.pop().setMap(null);
			}		
 
	var myLatLng = new google.maps.LatLng(locations_lat, locations_lng);
	userMarker[0] = new google.maps.Marker({
		position: myLatLng,
		map: map,
		icon: 'home-1.png'
	});
 
	var bounds = new google.maps.LatLngBounds();
    clearOverlay();
 
	var sidebar = $("#sidebar").html();
	$("#sidebar").html(sidebar);
 
	 //Загружаем данные в формате JSON из файла phpsqlsearch_genjson.php
		$.getJSON("phpsqlsearch_genjson.php", {lat : locations_lat, lng : locations_lng, radius:radius }, function(json){
 
			var marker;		
 
			if (json.length == null) {
				$("#sidebar").html('Ничего не найдено.');
				return;
			} 
 
 
		for (i = 0; i < json.length; i++) {
		var name = json[i].name;				
		var address = '<strong>'+name+'</strong><br>'+json[i].address+'<br >Район: '+json[i].rayon;
 
		var point = new google.maps.LatLng(json[i].lat, json[i].lng);         
		marker = createMarker(point, name, address);  
		bounds.extend(point);   
 
		}
 
		map.fitBounds(bounds);
		$("#sidebar").append(side_bar_html);	
 
		});		
 
       } else {
         alert(address + ' not found');
       }
     });
 
 
 
   }
 
function myclick(i) {
  var latLng = gmarkers[i].getPosition();
  map.setCenter(latLng);
  map.setZoom(17);
  google.maps.event.trigger(gmarkers[i], "click");
}
 
function clearOverlay() {
     infoWindow.close();
     for (var i = 0; i < gmarkers.length; i++) {
       gmarkers[i].setMap(null);
     }
     gmarkers.length = 0;
 
     side_bar_html = "";
     $("#sidebar").html("");
   } 
 
  </script> 
 
  <style type="text/css">
 
html, body { height: 100%; } 
 
font-family: Arial, Helvetica, sans-serif;
font-size: 10px;
</style>
 
</head> 
 
<body onload="initialize()"> 
<p>Ваш адрес: <input type="text" id="addressLoc" size="40" value="Нижний Новгород, "/> 
 Радиус поиска в км: <input type="text" id="radiusSelect" size="5"/> 
<input type="button" onclick="searchLocations()" value="Найти"/> 
</p> 
 
<div id="mymap_div" style="width:1050px;"> 
<div id="sidebar" style="float:left; width: 250px; height: 600px; overflow: auto;"></div> 
<div id="map" style="margin-left: 250px; width: 800px; height:600px;"></div> 
</div> 
 
</body> 
</html>

В начале мы после подключения API Google Maps, добавляем к нашему html-файлу javascript-библиотеку Jquery, для выполнения асинхронных запросов.
По клику на кнопке «Найти», вызывается функция searchLocations, в которой мы получаем введенные данные из формы, производим геокодирование адреса и отправляем запрос скрипту phpsqlsearch_genjson.php, а затем принимаем полученный ответ в формате JSON.

Функция createMarker — добавляет метку на карту и ссылку в слайдбар.

Функция myclick обрабатывает клик по ссылке в слайдбаре, происходит центрирование и масштабирование карты на нужной метке.

Функция clearOverlay — служит для удаления предыдущего результата поиска.

Скачать архив с файлами примера

Для написания заметки использовались примеры из следующих статей «Реализация поиска по данным пользователя с использованием API Google Maps v3» на моем блоге и «Creating a Store Locator with PHP, MySQL & Google Maps».

  • Гость: Отличная вещь, спасибо огромное, но есть вопрос... Как я понял, скрипт ищет не кафе, а данные из БД по координатам - то, что есть близко к объекту. Вопрос - необходимо самому создавать эту БД со всеми кафе или прочими центрами услуг? Или как-то интегрируются данные из данных Гугла? Например, я бы хотел, чтобы пользователь находил супермаркеты. В идеале, конечно, чтобы был выпадающий список (кафе, супермаркет, техобслуживание и т.д.), но думаю, что это тема отдельного урока. То есть суть вопроса - как и где находить необходимые объекты для БД? Спасибо!
  • Гость: Нужно создавать базу данных самому. Можно еще использовать сервис <a href="https://developers.google.com/maps/documentation/places/?hl=ru-RU" title="Google Places API" target="_blank" rel="nofollow">Google Places API</a>, но есть проблема с содержанием базы по городам России, если по Москве и Санкт-Петербургу много данных, то по другим городам они могут быть минимальными или вообще отсутствовать. Можно еще использовать <a href="http://api.2gis.ru/doc/firms/quickstart/" title="API 2GIS" target="_blank" rel="nofollow">API 2GIS</a>
  • Гость: Спасибо, будем пробовать! Наткнулся на урок по этому вопросу http://www.script-tutorials.com/google-places-api-practice/comment-page-1/ но почему-то не смог приспособить к своей местности - упрямо показывает только узкий спектр и только в том месте, про который пример (Нью-Йорк). Подозреваю, что где-то идет запрос в БД, но нигде в коде не смог обнаружить этого запроса. Если Вам будет интересно, то было бы прекрасно, если выложите здесь этот урок, думаю всем будет полезно. Спасибо за уроки!
  • Гость: Как переделать запрос чтобы выводились обекты которые лежат между двумя точками, учитывать например только долготу
  • Гость: В файле "phpsqlsearch_genjson.php" echo $json выводит null Может я что-то не так пишу в запросе? Не могли бы Вы расписать сам запрос более подробно? Что значит цифра 6371? И вообще вся эта длинная конструкция которая потом объявляется как distance - для чего это все? ) Спасибо.
  • Гость: И еще сразу вопрос: зачем нужны эти записи: $lat = $_GET["lat"]; $lng = $_GET["lng"]; $radius = $_GET["radius"]; Каким образом например все эти переменные окажутся в $_GET если мы туда ничего не передаем вроде...
  • Михаил Стефиенко: Простите за глупый вопрос. Но почему когда я скачал ваш пример, при его открытии и совершении поиска, не показываются метки кафе??? Но метка дома отображается(проверял на НН)