У меня есть проект, в котором мне нужно создать локатор магазина для клиента.
Я использую пользовательский тип сообщения » restaurant-location
«, и я написал код для геокодирования адресов, хранящихся в postmeta, с помощью Google Geocoding API (вот ссылка, которая геокодирует Белый дом США в JSON, и я сохранил широту и долготу назад в настраиваемые поля.
Я написал get_posts_by_geo_distance()
функцию, которая возвращает список постов в порядке ближайшего географического расположения, используя формулу, которую я нашел в слайд-шоу в этом посте. Вы можете вызвать мою функцию так (я начинаю с фиксированного «источника» lat/long):
include "wp-load.php";
$source_lat = 30.3935337;
$source_long = -86.4957833;
$results = get_posts_by_geo_distance(
'restaurant-location',
'geo_latitude',
'geo_longitude',
$source_lat,
$source_long);
echo '<ul>';
foreach($results as $post) {
$edit_url = get_edit_url($post->ID);
echo "<li>{$post->distance}: <a href="{$edit_url}" target="_blank">{$post->location}</a></li>";
}
echo '</ul>';
return;
Вот сама функция get_posts_by_geo_distance()
:
function get_posts_by_geo_distance($post_type,$lat_key,$lng_key,$source_lat,$source_lng) {
global $wpdb;
$sql =<<<SQL
SELECT
rl.ID,
rl.post_title AS location,
ROUND(3956*2*ASIN(SQRT(POWER(SIN(({$source_lat}-abs(lat.lat))*pi()/180/2),2)+
COS({$source_lat}*pi()/180)*COS(abs(lat.lat)*pi()/180)*
POWER(SIN(({$source_lng}-lng.lng)*pi()/180/2),2))),3) AS distance
FROM
wp_posts rl
INNER JOIN (SELECT post_id,CAST(meta_value AS DECIMAL(11,7)) AS lat FROM wp_postmeta lat WHERE lat.meta_key='{$lat_key}') lat ON lat.post_id = rl.ID
INNER JOIN (SELECT post_id,CAST(meta_value AS DECIMAL(11,7)) AS lng FROM wp_postmeta lng WHERE lng.meta_key='{$lng_key}') lng ON lng.post_id = rl.ID
WHERE
rl.post_type='{$post_type}' AND rl.post_name<>'auto-draft'
ORDER BY
distance
SQL;
$sql = $wpdb->prepare($sql,$source_lat,$source_lat,$source_lng);
return $wpdb->get_results($sql);
}
Меня беспокоит то, что SQL настолько неоптимизирован, насколько это возможно. MySQL не может упорядочивать по какому-либо доступному индексу, так как исходная география изменчива и нет конечного набора исходных гео для кэширования. В настоящее время я в тупике относительно способов его оптимизации.
Принимая во внимание то, что я уже сделал, возникает вопрос: как бы вы оптимизировали этот вариант использования?
Неважно, что я сохраняю все, что я сделал, если лучшее решение заставило бы меня выбросить это. Я открыт для рассмотрения практически любого решения, за исключением того, которое требует установки сервера Sphinx или чего-то еще, требующего индивидуальной конфигурации MySQL. По сути, решение должно работать с любой обычной установкой WordPress. (Тем не менее, было бы здорово, если бы кто-нибудь захотел перечислить альтернативные решения для других, которые могли бы стать более продвинутыми, и для потомков.)
Ресурсы найдены
К вашему сведению, я провел небольшое исследование по этому вопросу, поэтому вместо того, чтобы вы проводили исследование снова или вместо того, чтобы публиковать какие-либо из этих ссылок в качестве ответа, я продолжу и включу их.
- http://jebaird.com/blog/calculating-distance-miles-latitude-and-longitude
- http://wordpress.org/extend/plugins/геолокация/скриншоты/
- http://code.google.com/apis/maps/articles/phpsqlsearch.html
- http://www.rooftopsolutions.nl/blog/229
- http://планета.mysql.com/entry/?id=18085
- http://blog.peoplesdns.com/archives/24
- http://www.petefreitag.com/item/622.cfm
- http://www.phpro.org/tutorials/Geo-Targetting-With-PHP-And-MySQL.html
- http://forum.geonames.org/gforum/posts/list/692.page
- http://forums.mysql.com/list.php?23
- http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
- http://developer.yahoo.com/maps/rest/V1/geocode.html
- http://geocoder.us/
Относительно поиска Sphinx
- http://sphinxsearch.com/
- https://launchpad.net/wp-sphinx-плагин
- http://forums.site5.com/showthread.php?t=28981
- http://wordpress.org/extend/plugins/wordpress-sphinx-plugin/
- http://wordpress.org/extend/plugins/sphinx-search/
- http://www.mysqlperformanceblog.com/2008/02/15/mysql-performance-blog-now-uses-sphinx-for-site-search/
Какая точность вам нужна? если это поиск по штату/национальному масштабу, возможно, вы могли бы выполнить поиск по долготе и почтовому индексу и предварительно вычислить расстояние от области почтового индекса до области почтового индекса ресторана. Если вам нужны точные расстояния, это не будет хорошим вариантом.
Вам следует изучить решение Geohash, в статье Википедии есть ссылка на библиотеку PHP для кодирования декодирования lat long в geohash.
Здесь у вас есть хорошая статья, объясняющая, почему и как они используют его в Google App Engine (код Python, но его легко понять). Из-за необходимости использовать геохэш в GAE вы можете найти несколько хороших библиотек Python и примеры.
Как объясняется в этом сообщении в блоге, преимущество использования геохэшей заключается в том, что вы можете создать индекс в таблице MySQL для этого поля.
Спасибо за предложение по GeoHash! Я обязательно проверю это, но уезжаю в WordCamp Savannah через час, поэтому не могу сейчас. Это локатор ресторанов для туристов, посещающих город, поэтому минимальная точность, вероятно, будет 0,1 мили. В идеале было бы лучше. Я отредактирую ваши ссылки!
Если вы собираетесь отображать результаты на карте Google, вы можете использовать их API для сортировки code.google.com/apis/maps/documentation/mapsdata/…
Поскольку это самый интересный ответ, я приму его, хотя у меня не было времени изучить и попробовать его.
@Jan : Спасибо за ответ. Как вы думаете, вы можете предоставить реальный код, показывающий, как это реализовано?
@Mike: Это была интересная задача, но вот код, который должен работать.
@Jan Fabry: Круто! Я проверю это, когда вернусь к этому проекту.
Спасибо за продолжение. Я действительно пытался избежать добавления таблицы, но в итоге тоже добавил таблицу, хотя и попытался сделать ее более общей, чем конкретный вариант использования. Кроме того, я не использовал тип данных POINT, потому что хотел придерживаться более известных стандартных типов данных; Географические расширения MySQL требуют хорошего изучения, чтобы освоиться. Тем не менее, не могли бы вы обновить свой ответ, пожалуйста, с помощью DDL для вашей таблицы, которую вы использовали? Я думаю, что это будет поучительно для других, читающих это в будущем.
Это практически бесконечное количество записей…
инфа? Я вижу здесь только n^2, это не бесконечно. Особенно при увеличении количества записей следует все больше и больше учитывать предварительный расчет.
Практически бесконечно. Учитывая широту/долготу с точностью до 7 знаков после запятой, это даст 6,41977E+17 записей. Да, у нас не так много, но у нас было бы намного больше, чем было бы разумно.
Бесконечное — это четко определенный термин, и добавление к нему прилагательных мало что меняет. Но я знаю, что вы имеете в виду, вы думаете, что это слишком много для расчета. Если вы не добавляете плавно огромное количество новых местоположений с течением времени, этот предварительный расчет может быть выполнен шаг за шагом с помощью задания, работающего отдельно от вашего приложения в фоновом режиме. Точность не влияет на количество вычислений. Количество локаций делает. Но, возможно, я неправильно прочитал эту часть вашего комментария. Например, 64 местоположения приведут к 4 096 (или 4 032 для n*(n-1) ) вычислений и, следовательно, записей.
Это может быть слишком поздно для вас, но я все равно отвечу тем же ответом, что и на этот связанный вопрос, чтобы будущие посетители могли ссылаться на оба вопроса.
Я бы не стал хранить эти значения в таблице метаданных поста или, по крайней мере, не только там. Вам нужна таблица со столбцами
post_id
,lat
,lon
, чтобы вы могли поместить в нее индексlat, lon
и запрос. Это не должно быть слишком сложно поддерживать в актуальном состоянии с помощью функции сохранения и обновления после публикации.Когда вы запрашиваете базу данных, вы определяете ограничивающую рамку вокруг начальной точки, поэтому вы можете сделать эффективный запрос для всех
lat, lon
пар между границами север-юг и восток-запад рамки.После того, как вы получите этот сокращенный результат, вы можете выполнить более сложный расчет расстояния (круговое или фактическое направление движения), чтобы отфильтровать местоположения, которые находятся в углах ограничивающей рамки и, следовательно, дальше, чем вы хотите.
Здесь вы найдете простой пример кода, который работает в админке. Вам нужно создать дополнительную таблицу базы данных самостоятельно. Код упорядочен от наиболее интересного к наименее интересному.
Я опаздываю на эту вечеринку, но оглядываясь назад, я понимаю,
get_post_meta
что проблема действительно здесь, а не в SQL-запросе, который вы используете.Недавно мне пришлось выполнить аналогичный поиск географических данных на сайте, который я запускаю, и вместо того, чтобы использовать метатаблицу для хранения широты и долготы (что требует в лучшем случае двух объединений для поиска и, если вы используете get_post_meta, две дополнительные базы данных запросов на местоположение), я создал новую таблицу с пространственно-индексированным геометрическим типом данных POINT.
Мой запрос был очень похож на ваш, с MySQL, выполняющим большую часть тяжелой работы (я не использовал триггерные функции и упростил все до двумерного пространства, потому что это было достаточно близко для моих целей):
где $client_location — это значение, возвращаемое общедоступной службой поиска IP-адресов (я использовал geoio.com, но есть несколько подобных).
Это может показаться громоздким, но при тестировании он постоянно возвращал ближайшие 5 местоположений из таблицы с 80 000 строк менее чем за 0,4 секунды.
Пока MySQL не развернет предлагаемую функцию DISTANCE, это кажется лучшим способом, который я нашел для реализации поиска местоположения.
РЕДАКТИРОВАТЬ: добавление структуры таблицы для этой конкретной таблицы. Это набор списков свойств, поэтому он может быть или не быть похожим на любой другой вариант использования.
Столбец
geolocation
— единственное, что имеет отношение к нашим целям; он состоит из координат x(lon),y(lat), которые я просто ищу по адресу при импорте новых значений в базу данных.Просто предварительно рассчитайте расстояния между всеми объектами. Я бы сохранил это в таблице базы данных отдельно с возможностью индексации значений.