Есть в Друпале широко известный модуль Geofield, полезный для хранения информации о местоположении. В смысле, широты и долготы того или иного объекта. Помимо просто хранения (например, для вывода на карту), модуль умеет и вычислять расстояние между двумя точками по широте и долготе. Для этого используется известная формула гаверсинусов, дающая вполне приемлемую погрешность для бытового применения.
Для вычисления расстояний имеется интеграция во Views, то есть можно выводить расстояния как поля вьюза, можно сортировать по расстоянию. Но что если нам нужно обойтись без Views и добавить расчет расстояния в собственный запрос? Чтобы не дублировать функционал, можно воспользоваться функцией geofield_haversine()
.
Функция генерирует SQL-формулу для вставки вычисления расстояния прямо в запрос. Вот пример кода для выборки nid
материалов, отсортированных по возрастанию расстояния до заданной точки.
$query = db_select('node', 'n');
$query->fields('n', array('nid'));
$query->join('field_data_field_location', 'l', 'n.nid = l.entity_id');
$query->condition('l.entity_type', 'node');
$haversine_args = array(
'earth_radius' => GEOFIELD_KILOMETERS,
'origin_latitude' => $lat,
'origin_longitude' => $lon,
'destination_latitude' => 'l.field_location_lat',
'destination_longitude' => 'l.field_location_lon',
);
$query->orderBy(geofield_haversine($haversine_args));
$nids = $query->execute()->fetchCol();
Код написан исходя из того, что координаты хранятся в поле field_location
. В аргументах origin_latitude
и origin_longitude
передается широта и долгота заданной точки, а в earth_radius
, конечно, радиус Земли. Передавать его нужно в тех единицах измерения, в которых хочется получить результат. В данном случае — в километрах, но в модуле geofield также заданы значения для метров, миль, ярдов, футов и морских миль.