Имеются ноды с полями широты и долготы. При открытии конкретной ноды, хотелось бы показать ближайшие точки (ноды) отобрав их по широте и долготе.
Есть мысли рассчитать некую дистанцию от нулевой широты и долготы до текущей ноды. Записать значение ноды в новое поле и на основе этой дистанции брать соседние. Например текущее значение дистанции 100 для ноды (значение просто с головы) и делать выборку нод от 90 до 110 по дистанции.
Вопрос, по какой формуле считать это самое значение дистанции? или может есть другие варианты
Комментарии
https://www.drupal.org/project/geolocation_proximity
Спасибо, вечером попробую.
а пока нашел вот такую штуку http://sunnyblik.livejournal.com/29267.html и там есть запрос SELECT * FROM `metro` WHERE `x` BETWEEN '0' AND '6' AND `y` BETWEEN '0' AND '6', есть мысли использовать его если ничего не выйдет
все выйдет.
есть одна проблемка, ноды уже заполнены (150 тыс), но вот для координат не использовалось Geolocation Field
а что использовалось?
обычные поля текстовые или float... все сделано отметки текущей точки на карте есть, да вот понадобилось соседние выводить((
буду думать как перегнать их в гео филд
<?php
$query = new EntityFieldQuery();
$result = $query->entityCondition('entity_type', 'node')
->entityCondition('bundle', 'mycontenttype')
->execute();
$nodes = node_load_multiple(array_keys($result['node']));
foreach($nodes as $node){
$old_data = $node->field_old_geo['und'][0]['value'];
$geo = explode(',',$old_date);
$node->field_coordinates[LANGUAGE_NONE][0] = geofield_compute_values(
array(
'lat' => $geo[0],
'lon' => $geo[1],
), GEOFIELD_INPUT_LAT_LON);
}
node_save($node);
?>
как то так
ок, понял. Спасибо, вечером будут испытания)
только нужно будет разбить думаю на более мелкие операции вот это node_load_multiple, 150 тыс многовато за один раз, как бы не загнулся скрипт
напишите свой script.php и запустите через консоль. будет быстрее чем batch писать
hook_action_info и модуль VBO спасут отца русской демократии..
https://api.drupal.org/api/drupal/modules!system!system.api.php/function...
https://www.drupal.org/node/2052067
Объявить в хуке функцию, реализующую action.
Вставить в нее, немного адаптировав, приведенный выше код..
Сделать вьюс для выборки нужных материалов.
Добавить к нему поле VBO и выбрать созданный ранее action.
и запустить action на выполнение.
В общем на "говнокодил" по другому...)
Сделал выборку 10 значений больше текущих координат с сортировкой по широте и долготе:
<?php$query = db_select('node','n');
$query->join('field_data_field__coordinates_d','d','d.entity_id = n.nid');
$query->join('field_data_field__coordinates_s','s','s.entity_id = n.nid');
$query->fields('n',array('nid'))
->fields('d',array('field__coordinates_d_value'))
->fields('s',array('field__coordinates_s_value'))
->condition('s.field__coordinates_s_value', array($current_s), '>=')
->condition('d.field__coordinates_d_value', array($current_d), '>=')
->condition('n.nid', array($current_nid), '<>')
->range(0, 10)
->orderBy('d.field__coordinates_d_value', 'ASC')
->orderBy('s.field__coordinates_s_value', 'ASC');
$results_p = $query->execute()->fetchAll();?>
Далее выбрал 10 значений меньше текущих координат:
<?php$query = db_select('node','n');
$query->join('field_data_field__coordinates_d','d','d.entity_id = n.nid');
$query->join('field_data_field__coordinates_s','s','s.entity_id = n.nid');
$query->fields('n',array('nid'))
->fields('d',array('field__coordinates_d_value'))
->fields('s',array('field__coordinates_s_value'))
->condition('s.field__coordinates_s_value', array($current_s), '<=')
->condition('d.field__coordinates_d_value', array($current_d), '<=')
->condition('n.nid', array($current_nid), '<>')
->range(0, 10)
->orderBy('d.field__coordinates_d_value', 'DESC')
->orderBy('s.field__coordinates_s_value', 'DESC');
$results_m = $query->execute()->fetchAll();?>
После объединил массивы вместе:
<?php$results = array_merge($results_p, $results_m);?>
И начал считать дистанцию между текущей точкой и 20-ти выбранных:
<?phpforeach($results as $result){
$dist = round(distance($current_s, $current_d, $result->field__coordinates_s_value, $result->field__coordinates_d_value), 2);
//dpm($dist);
if(isset($dist)){
$distance_a[$result->nid]['nid'] = $result->nid;
$distance_a[$result->nid]['dist'] = $dist;
$title = 'Title';
$distance_a[$result->nid]['title'] = $title;
}
//
}?>
Функция distance выглядит так (возвращает расстояние между точками в км):
<?phpfunction distance($lat1, $lon1, $lat2, $lon2) {
$theta = $lon1 - $lon2;
$dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
$dist = acos($dist);
$dist = rad2deg($dist);
return $km = $dist * 60 * 1.1515 * 1.609344;//без умножения на 1.609344 - будут мили
}?>
В итоге получили массив нод с id и расстоянием (дистанциями) до текущей точки
Далее нужно отсортировать все это по возрастанию дистанции и запихать в вывод foreach
Сортируем пользовательской функцией по ключу двумерного массива dist
<?php//for sort http://php.net/manual/ru/function.array-multisort.php
function array_orderby(){
$args = func_get_args();
$data = array_shift($args);
foreach ($args as $n => $field) {
if (is_string($field)) {
$tmp = array();
foreach ($data as $key => $row)
$tmp[$key] = $row[$field];
$args[$n] = $tmp;
}
}
$args[] = &$data;
call_user_func_array('array_multisort', $args);
return array_pop($args);
}?>
Вот так вызываем: $distance_a = array_orderby($distance_a, 'dist', SORT_ASC);
Ну и в конце выводим 10 значений из 20:
<?php$count = 1;
foreach($distance_a as $distance){
$block['content'] .= '<li>'.l($distance['title'].' ('.$distance['dist'].'km)', 'node/'.$distance['nid']).'</li>';
if($count >= 10) break;
else $count++;
}?>
Для большей точности можно выбирать из БД больше значений, а не по 10 больше и меньше текущих координат
+ получаем дистанцию для вывода до ближайших точек
Сейчас думаю по поводу кеша для запросов, ну и выслушаю комменты по коду.
Модуль указанный выше не стал использовать, вышло бы дольше, да и не понимал бы я как это работает
Результат:
Тоже решение
Добавил кеширование т.к. 5-6 джоинов в запросе
Первая загрузка Executed 288 queries in 4297.82 ms. Queries exceeding 5 ms are highlighted. Page execution time was 4668.82 ms.
из кеша Executed 188 queries in 14.22 ms. Queries exceeding 5 ms are highlighted. Page execution time was 313.67 ms.
<?php$cache = cache_get('proximity_location_plus_'.$current_nid, 'cache');
$results_p = array();
if (!empty($cache->data)) {
$results_p = $cache->data;
}else{
//здесь 1-й код из предыдущего моего коммента
cache_set('proximity_location_plus_'.$current_nid, $results_p, 'cache', CACHE_PERMANENT);
}?>
и для второго запроса аналогично.
Заодно и с кешированием разобрался наконец-то
UPD тест показал, что стандартный кеш отлично кеширует все запросы страницы и код выше не нужен