Клуб API Карт

Неверные расстояния

weautorf39
22 октября 2012, 12:36

Здравствуйте! Подскажите, пожалуйста. Задача такая: человек вводит свой адрес, выбирает радиус и на карте показываются ближайшие от него точки. Но происходит какая-то мистика! Например, яндекс прокладывает по дороге маршрут длиной 2 км. Я выбираю радиус 3 км - точка не показывается. Выбираю 5 км - показывается.

Для решения своей задачи я взял за основу пример: http://webmap-blog.ru/yandex-maps/poisk-blizhajshego-obekta-na-yandeks-karte.

Вот что получилось у меня: http://mymacros.ru/raznoe/ymap-location3.html

 

Формула определения расстояния между двумя координатами вроде правильная... Почему же так происходит? 

Подскажите, пожалуйста, сам я не большой специалист... только начинаю разбираться в этой теме.

Спасибо!

6 комментариев
Подписаться на комментарии к посту

я когда-то использовал такую формулу

function Pereobraz_koord ($StartLat, $EndLat, $StartLong, $EndLong){
 $D2R=0.017453;   // Константа для преобразования градусов в радианы
 $R2D=57.295781;   // Константа для преобразования радиан в градусы
 $a=6378137.0;   // Основные полуоси
 $b=6357752.314245;  // Неосновные полуоси
 $e2=0.006739496742337; // Квадрат эксцентричности эллипсоида
 $f=0.003352810664747; // Выравнивание эллипсоида
 // Вычисляем разницу между двумя долготами
 //и широтами, и получаем среднюю широту
 $fdLambda=($StartLong-$EndLong)*$D2R;
$fdLambda;
 $fdPhi=($StartLat-$EndLat)*$D2R;
 $fPhimean=(($StartLat+$EndLat)/2.0)*$D2R;
 // Вычисляем меридианные
 //и поперечные радиусы кривизны средней широты
 $fTemp=1-$e2*(pow(sin($fPhimean),2));
$fRho=($a*(1-$e2))/pow($fTemp,1.5);
 $fNu=$a/(sqrt(1-$e2*(sin($fPhimean)*sin($fPhimean))));
// Вычисляем угловое расстояние
 $fz=sqrt(pow(sin($fdPhi/2.0),2)+cos($EndLat*$D2R)*cos($StartLat*$D2R)*pow(sin($fdLambda/2.0),2));
 $fz=2*asin($fz);
    // Вычисляем смещение
 $fAlpha=cos($EndLat*$D2R)*sin($fdLambda)*1/sin($fz);
 $fAlpha=asin($fAlpha);
 // Вычисляем радиус Земли
 $fR=($fRho*$fNu)/(($fRho*pow(sin($fAlpha),2))+($fNu*pow(cos($fAlpha),2)));
$Distance=$fz*$fR;
 global $rastoianie;
$rastoianie=$Distance;
$rastoianie = round($rastoianie, 2);
return $rastoianie/1000;
 }

 

 

Спасибо большое! Попробую. А как можно эту формулу вставить в файл poisk.php? Просто ведь там запрос к базе происходит. Как их соединить?

я просто вытаскивал все значения а потом перебирал среди всех которые подходят по растоянию

if($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
$center=$_POST['center'];
$center_y = $center[0];
$center_x = $center[1];
$radius = $_POST["radius"];
$json = '{"markers" :['."\n";
$resso = mysql_query("SELECT * FROM metki");
  $mar= mysql_fetch_assoc($resso);
  if($mar){
        do{
            $rasto= Pereobraz_koord ($mar['x'], $center_x,$mar['y'],$center_y);
            if($radius>=$rasto){
                $json =  array('id'=>$mar['id'],'x'=>$mar['x'], 'y'=>$mar['y'],'adres'=>$mar['adres'],'type'=>$mar['type'],'name'=>$mar['name']);
                $markers[] = $json; 
            }
        }while($mar = mysql_fetch_assoc($resso));
  }
$points = array('markers'=>$markers);
echo json_encode($points);
}

 

но мне кажеться вам лучше перейти на API 2.0 там мне кажеться всё можно проще реализовать.

то, что написано Evgeniy - неправильно.
вам тогда придется делать full scan таблицы с точками и каждую прогонять через вычисление расстояния до точки!!!
делать надо так:
1. вычислить mbr вашего круга (то есть координаты прямоугольника, ограничивающего ваш круг)
получим [[lat1, long2], [lat2, long2]].
2. делаем простой запрос к базе с where lat Between (lat1, lat2) AND lng between(lng1, lng2).
3. полученный результат можно прогнать через формулу Evgeniy, а можно просто сделать подзапрос типа:
$query = sprintf("SELECT id, address, name, 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
WHERE id IN (SELECT id FROM markers WHERE (lat BETWEEN lat1 AND lat2) AND (lng BETWEEN lng1 AND lng2))
HAVING distance < '%s'  ORDER BY distance LIMIT 0, 20"

за такой вот такой запрос - убивать нада ))) $query = sprintf("SELECT address, name, 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 HAVING distance < '%s' ORDER BY distance LIMIT 0, 20"
там вычислений море, фулскан и не индексируется никак.

Спасибо большое! А подскажите, это решит мою проблему или просто уменьшит вычисления? И координаты этого прямоугольника я должен выбрать так, чтобы в него попали все мои точки, но небольше. Правильно? 

А то у меня получается, что до АЗС по извилистой дороге 154 км, а она не попадает в радиус 150 и даже 200.  

Дело в том, что Ваш пример написан на первой версии api , а сейчас уже год почти как вторая.
поэтому сложно разобраться, где у Вас ошибка. скорее всего ошибка именно в координатах.
проверьте, что передаются именно lat - это широта, и lng - это долгота.
второе, что может быть, проверьте, что в mysql передается именно float, у Вас может быть настройка в php стоит, что float через запятую, например.
вот тут надо по идее делать cast

$center_lat = (float) $exp_str1[0];
$center_lng = (float) $exp_str1[1];
$radius = (int) $_GET["radius"];
потом проверить
if (!(is_float($center_lat) && is_float($center_lng) && is_int($radius))) {
die('ошибка параметров"
}

что касается моего поста, то если у вас сотня точек, париться не стоит, используйте то, что работает. просто сейчас у меня проект с 300000 точек. вот там такое непозволительно.