이것은 너무 늦을 수도 있지만 어쨌든 이 관련 질문에 대한 답변과 비슷한 대답으로 대답 할 것이므로 미래 방문자가 두 질문을 모두 참조 할 수 있습니다.
나는 적어도하지 포스트 메타 데이터 테이블에이 값을 저장하거나하지 않을 경우에만 이. 당신이있는 테이블을 원하는 post_id
, lat
, lon
열, 그래서 당신은의 인덱스를 배치 할 수 있습니다 lat, lon
그것에 대한 쿼리를. 사후 저장 및 업데이트에 대한 후크로 최신 상태를 유지하기가 너무 어렵지 않아야합니다.
데이터베이스를 쿼리 할 때 시작점 주위에 경계 상자 를 정의하면 상자lat, lon
의 남북 경계와 동서 경계 사이의 모든 쌍에 대해 효율적인 쿼리를 수행 할 수 있습니다 .
이 축소 된 결과를 얻은 후에는 경계 상자의 모서리에있는 위치를 필터링하여 원하는 것보다 더 멀리있는 고급 (원형 또는 실제 주행 방향) 거리 계산을 수행 할 수 있습니다.
다음은 관리 영역에서 작동하는 간단한 코드 예제입니다. 추가 데이터베이스 테이블을 직접 작성해야합니다. 이 코드는 가장 흥미로운 것부터 가장 재미없는 것으로 주문됩니다.
<?php
/*
Plugin Name: Monkeyman geo test
Plugin URI: http://www.monkeyman.be
Description: Geolocation test
Version: 1.0
Author: Jan Fabry
*/
class Monkeyman_Geo
{
public function __construct()
{
add_action('init', array(&$this, 'registerPostType'));
add_action('save_post', array(&$this, 'saveLatLon'), 10, 2);
add_action('admin_menu', array(&$this, 'addAdminPages'));
}
/**
* On post save, save the metadata in our special table
* (post_id INT, lat DECIMAL(10,5), lon DECIMAL (10,5))
* Index on lat, lon
*/
public function saveLatLon($post_id, $post)
{
if ($post->post_type != 'monkeyman_geo') {
return;
}
$lat = floatval(get_post_meta($post_id, 'lat', true));
$lon = floatval(get_post_meta($post_id, 'lon', true));
global $wpdb;
$result = $wpdb->replace(
$wpdb->prefix . 'monkeyman_geo',
array(
'post_id' => $post_id,
'lat' => $lat,
'lon' => $lon,
),
array('%s', '%F', '%F')
);
}
public function addAdminPages()
{
add_management_page( 'Quick location generator', 'Quick generator', 'edit_posts', __FILE__ . 'generator', array($this, 'doGeneratorPage'));
add_management_page( 'Location test', 'Location test', 'edit_posts', __FILE__ . 'test', array($this, 'doTestPage'));
}
/**
* Simple test page with a location and a distance
*/
public function doTestPage()
{
if (!array_key_exists('search', $_REQUEST)) {
$default_lat = ini_get('date.default_latitude');
$default_lon = ini_get('date.default_longitude');
echo <<<EOF
<form action="" method="post">
<p>Center latitude: <input size="10" name="center_lat" value="{$default_lat}"/>
<br/>Center longitude: <input size="10" name="center_lon" value="{$default_lon}"/>
<br/>Max distance (km): <input size="5" name="max_distance" value="100"/></p>
<p><input type="submit" name="search" value="Search!"/></p>
</form>
EOF;
return;
}
$center_lon = floatval($_REQUEST['center_lon']);
$center_lat = floatval($_REQUEST['center_lat']);
$max_distance = floatval($_REQUEST['max_distance']);
var_dump(self::getPostsUntilDistanceKm($center_lon, $center_lat, $max_distance));
}
/**
* Get all posts that are closer than the given distance to the given location
*/
public static function getPostsUntilDistanceKm($center_lon, $center_lat, $max_distance)
{
list($north_lat, $east_lon, $south_lat, $west_lon) = self::getBoundingBox($center_lat, $center_lon, $max_distance);
$geo_posts = self::getPostsInBoundingBox($north_lat, $east_lon, $south_lat, $west_lon);
$close_posts = array();
foreach ($geo_posts as $geo_post) {
$post_lat = floatval($geo_post->lat);
$post_lon = floatval($geo_post->lon);
$post_distance = self::calculateDistanceKm($center_lat, $center_lon, $post_lat, $post_lon);
if ($post_distance < $max_distance) {
$close_posts[$geo_post->post_id] = $post_distance;
}
}
return $close_posts;
}
/**
* Select all posts ids in a given bounding box
*/
public static function getPostsInBoundingBox($north_lat, $east_lon, $south_lat, $west_lon)
{
global $wpdb;
$sql = $wpdb->prepare('SELECT post_id, lat, lon FROM ' . $wpdb->prefix . 'monkeyman_geo WHERE lat < %F AND lat > %F AND lon < %F AND lon > %F', array($north_lat, $south_lat, $west_lon, $east_lon));
return $wpdb->get_results($sql, OBJECT_K);
}
/* Geographical calculations: distance and bounding box */
/**
* Calculate the distance between two coordinates
* http://stackoverflow.com/questions/365826/calculate-distance-between-2-gps-coordinates/1416950#1416950
*/
public static function calculateDistanceKm($a_lat, $a_lon, $b_lat, $b_lon)
{
$d_lon = deg2rad($b_lon - $a_lon);
$d_lat = deg2rad($b_lat - $a_lat);
$a = pow(sin($d_lat/2.0), 2) + cos(deg2rad($a_lat)) * cos(deg2rad($b_lat)) * pow(sin($d_lon/2.0), 2);
$c = 2 * atan2(sqrt($a), sqrt(1-$a));
$d = 6367 * $c;
return $d;
}
/**
* Create a box around a given point that extends a certain distance in each direction
* http://www.colorado.edu/geography/gcraft/warmup/aquifer/html/distance.html
*
* @todo: Mind the gap at 180 degrees!
*/
public static function getBoundingBox($center_lat, $center_lon, $distance_km)
{
$one_lat_deg_in_km = 111.321543; // Fixed
$one_lon_deg_in_km = cos(deg2rad($center_lat)) * 111.321543; // Depends on latitude
$north_lat = $center_lat + ($distance_km / $one_lat_deg_in_km);
$south_lat = $center_lat - ($distance_km / $one_lat_deg_in_km);
$east_lon = $center_lon - ($distance_km / $one_lon_deg_in_km);
$west_lon = $center_lon + ($distance_km / $one_lon_deg_in_km);
return array($north_lat, $east_lon, $south_lat, $west_lon);
}
/* Below this it's not interesting anymore */
/**
* Generate some test data
*/
public function doGeneratorPage()
{
if (!array_key_exists('generate', $_REQUEST)) {
$default_lat = ini_get('date.default_latitude');
$default_lon = ini_get('date.default_longitude');
echo <<<EOF
<form action="" method="post">
<p>Number of posts: <input size="5" name="post_count" value="10"/></p>
<p>Center latitude: <input size="10" name="center_lat" value="{$default_lat}"/>
<br/>Center longitude: <input size="10" name="center_lon" value="{$default_lon}"/>
<br/>Max distance (km): <input size="5" name="max_distance" value="100"/></p>
<p><input type="submit" name="generate" value="Generate!"/></p>
</form>
EOF;
return;
}
$post_count = intval($_REQUEST['post_count']);
$center_lon = floatval($_REQUEST['center_lon']);
$center_lat = floatval($_REQUEST['center_lat']);
$max_distance = floatval($_REQUEST['max_distance']);
list($north_lat, $east_lon, $south_lat, $west_lon) = self::getBoundingBox($center_lat, $center_lon, $max_distance);
add_action('save_post', array(&$this, 'setPostLatLon'), 5);
$precision = 100000;
for ($p = 0; $p < $post_count; $p++) {
self::$currentRandomLat = mt_rand($south_lat * $precision, $north_lat * $precision) / $precision;
self::$currentRandomLon = mt_rand($west_lon * $precision, $east_lon * $precision) / $precision;
$location = sprintf('(%F, %F)', self::$currentRandomLat, self::$currentRandomLon);
$post_data = array(
'post_status' => 'publish',
'post_type' => 'monkeyman_geo',
'post_content' => 'Point at ' . $location,
'post_title' => 'Point at ' . $location,
);
var_dump(wp_insert_post($post_data));
}
}
public static $currentRandomLat = null;
public static $currentRandomLon = null;
/**
* Because I didn't know how to save meta data with wp_insert_post,
* I do it here
*/
public function setPostLatLon($post_id)
{
add_post_meta($post_id, 'lat', self::$currentRandomLat);
add_post_meta($post_id, 'lon', self::$currentRandomLon);
}
/**
* Register a simple post type for us
*/
public function registerPostType()
{
register_post_type(
'monkeyman_geo',
array(
'label' => 'Geo Location',
'labels' => array(
'name' => 'Geo Locations',
'singular_name' => 'Geo Location',
'add_new' => 'Add new',
'add_new_item' => 'Add new location',
'edit_item' => 'Edit location',
'new_item' => 'New location',
'view_item' => 'View location',
'search_items' => 'Search locations',
'not_found' => 'No locations found',
'not_found_in_trash' => 'No locations found in trash',
'parent_item_colon' => null,
),
'description' => 'Geographical locations',
'public' => true,
'exclude_from_search' => false,
'publicly_queryable' => true,
'show_ui' => true,
'menu_position' => null,
'menu_icon' => null,
'capability_type' => 'post',
'capabilities' => array(),
'hierarchical' => false,
'supports' => array(
'title',
'editor',
'custom-fields',
),
'register_meta_box_cb' => null,
'taxonomies' => array(),
'permalink_epmask' => EP_PERMALINK,
'rewrite' => array(
'slug' => 'locations',
),
'query_var' => true,
'can_export' => true,
'show_in_nav_menus' => true,
)
);
}
}
$monkeyman_Geo_instance = new Monkeyman_Geo();