¿Cómo puedo implementar una búsqueda basada en ubicación (código postal) en WordPress?

19

Estoy trabajando en un sitio de directorio de negocios local que usará tipos de publicaciones personalizados para las entradas de negocios. Uno de los campos será "Código Postal". ¿Cómo puedo configurar una búsqueda basada en la ubicación?

Me gustaría que los visitantes puedan ingresar su código postal y elegir una categoría y mostrar todos los negocios dentro de un cierto radio, o todos los negocios ordenados por distancia. Vi un par de complementos que dicen hacer esto pero no son compatibles con WordPress 3.0. ¿Alguna sugerencia?

    
pregunta matt 28.09.2010 - 00:32

3 respuestas

10

Yo modificaría la respuesta de gabrielk y la publicación del blog vinculado utilizando índices de base de datos y minimizando el número de cálculos de distancia real .

Si conoce las coordenadas del usuario y conoce la distancia máxima (por ejemplo, 10 km), puede dibujar un cuadro delimitador de 20 km por 20 km con la ubicación actual en el centro. Obtenga estas coordenadas de límite y las consultas solo se almacenan entre estas latitudes y longitudes . Aún no use las funciones de trigonometría en su consulta de base de datos, ya que esto evitará que se usen los índices. (Por lo tanto, puede obtener una tienda que esté a 12 km de distancia de usted si está en la esquina noreste del cuadro delimitador, pero la desechamos en el siguiente paso).

Calcule solo la distancia (a medida que el ave vuela o con las instrucciones de manejo reales, como prefiera) para las pocas tiendas que se devuelven. Esto mejorará drásticamente el tiempo de procesamiento si tiene una gran cantidad de tiendas.

Para la búsqueda relacionada ( "proporcione las diez tiendas más cercanas" ), puede hacer una búsqueda similar, pero con una estimación de la distancia inicial (así que comienza con un área de 10 km por 10 km, y si si no tiene suficientes tiendas, puede ampliarlo a 20 km por 20 km y así sucesivamente). Para esta conjetura de distancia inicial, una vez calcule el número de tiendas sobre el área total y úselo. O registre la cantidad de consultas que necesita y adáptese con el tiempo.

Agregué una Ejemplo de código completo en la pregunta relacionada de Mike , y aquí hay una extensión que le brinda las ubicaciones X más cercanas (rápida y apenas probada):

class Monkeyman_Geo_ClosestX extends Monkeyman_Geo
{
    public static $closestXStartDistanceKm = 10;
    public static $closestXMaxDistanceKm = 1000; // Don't search beyond this

    public function addAdminPages()
    {
        parent::addAdminPages();
        add_management_page( 'Location closest test', 'Location closest test', 'edit_posts', __FILE__ . 'closesttest', array(&$this, 'doClosestTestPage'));
    }

    public function doClosestTestPage()
    {
        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>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}"/></p>
    <p><input type="submit" name="search" value="Search!"/></p>
</form>
EOF;
            return;
        }
        $post_count = intval($_REQUEST['post_count']);
        $center_lon = floatval($_REQUEST['center_lon']);
        $center_lat = floatval($_REQUEST['center_lat']);

        var_dump(self::getClosestXPosts($center_lon, $center_lat, $post_count));
    }

    /**
     * Get the closest X posts to a given location
     *
     * This might return more than X results, and never more than
     * self::$closestXMaxDistanceKm away (to prevent endless searching)
     * The results are sorted by distance
     *
     * The algorithm starts with all locations no further than
     * self::$closestXStartDistanceKm, and then grows this area
     * (by doubling the distance) until enough matches are found.
     *
     * The number of expensive calculations should be minimized.
     */
    public static function getClosestXPosts($center_lon, $center_lat, $post_count)
    {
        $search_distance = self::$closestXStartDistanceKm;
        $close_posts = array();
        while (count($close_posts) < $post_count && $search_distance < self::$closestXMaxDistanceKm) {
            list($north_lat, $east_lon, $south_lat, $west_lon) = self::getBoundingBox($center_lat, $center_lon, $search_distance);

            $geo_posts = self::getPostsInBoundingBox($north_lat, $east_lon, $south_lat, $west_lon);


            foreach ($geo_posts as $geo_post) {
                if (array_key_exists($geo_post->post_id, $close_posts)) {
                    continue;
                }
                $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 < $search_distance) {
                    // Only include those that are in the the circle radius, not bounding box, otherwise we might miss some closer in the next step
                    $close_posts[$geo_post->post_id] = $post_distance;
                }
            }

            $search_distance *= 2;
        }

        asort($close_posts);

        return $close_posts;
    }

}

$monkeyman_Geo_ClosestX_instace = new Monkeyman_Geo_ClosestX();
    
respondido por el Jan Fabry 07.10.2010 - 11:52
8

Primero necesitas una tabla que se vea así:

zip_code    lat     lon
10001       40.77    73.98

... rellenado para cada código postal. Puede ampliar esto agregando campos de ciudad y estado si desea buscar de esa manera.

Luego, a cada tienda se le puede dar un código postal, y cuando necesite calcular la distancia, puede unir la tabla de latitud / longitud a los datos de la tienda.

Luego, consultará esa tabla para obtener la latitud y la longitud de los códigos postales de la tienda y del usuario. Una vez que lo obtenga, puede rellenar su matriz y pasarla a la función "obtener distancia":

$user_location = array(
    'latitude' => 42.75,
    'longitude' => 73.80,
);

$output = array();
$results = $wpdb->get_results("SELECT id, zip_code, lat, lon FROM store_table");
foreach ( $results as $store ) {
    $store_location = array(
        'zip_code' => $store->zip_code, // 10001
        'latitude' => $store->lat, // 40.77
        'longitude' => $store->lon, // 73.98
    );

    $distance = get_distance($store_location, $user_location, 'miles');

    $output[$distance][$store->id] = $store_location;
}

ksort($output);

foreach ($output as $distance => $store) {
    foreach ( $store as $id => $location ) {
        echo 'Store ' . $id . ' is ' . $distance . ' away';
    }
}

function get_distance($store_location, $user_location, $units = 'miles') {
    if ( $store_location['longitude'] == $user_location['longitude'] &&
    $store_location['latitude'] == $user_location['latitude']){
        return 0;

    $theta = ($store_location['longitude'] - $user_location['longitude']);
    $distance = sin(deg2rad($store_location['latitude'])) * sin(deg2rad($user_location['latitude'])) + cos(deg2rad($store_location['latitude'])) * cos(deg2rad($user_location['latitude'])) * cos(deg2rad($theta));
    $distance = acos($distance);
    $distance = rad2deg($distance);
    $distance = $distance * 60 * 1.1515;

    if ( 'kilometers' == $units ) {
        $distance = $distance * 1.609344;
    }

    return round($distance);
}

Esto se entiende como una prueba de concepto, no un código que realmente recomendaría implementar. Si tiene 10,000 tiendas, por ejemplo, sería una operación bastante costosa consultarlas todas, hacer un ciclo y ordenarlas en cada solicitud.

    
respondido por el gabrielk 05.10.2010 - 00:04
3

La documentación de MySQL también incluye información sobre extensiones espaciales. Curiosamente, la función de distancia estándar () no está disponible, pero consulte esta página: enlace para obtener detalles sobre cómo "convertir los dos valores de PUNTO a un LINESTRING y luego calcular la longitud de eso".

Tenga en cuenta que es probable que cada proveedor ofrezca diferentes latitudes y longitudes que representan el "centroide" de un código postal. También vale la pena saber que no hay archivos de "límite" de código postal realmente definidos. Cada proveedor tendrá su propio conjunto de límites que coinciden aproximadamente con las listas específicas de direcciones que conforman un código postal de USPS. (Por ejemplo, en algunos "límites" tendría que incluir ambos lados de la calle, en otros, solo uno.) Áreas de tabulación de códigos postales (ZCTA), ampliamente utilizadas por los proveedores, "no representan con precisión las áreas de entrega de códigos postales. y no incluya todos los códigos postales utilizados para la entrega de correo " enlace

Muchas empresas del centro tendrán su propio código postal. Querrá un conjunto de datos lo más completo posible, así que asegúrese de encontrar una lista de códigos postales que incluya tanto códigos postales de "punto" (generalmente empresas) como códigos postales de "límite".

Tengo experiencia trabajando con los datos de código postal de enlace . Incluso eso no era 100% exacto. Por ejemplo, cuando mi campus adquirió una nueva dirección de correo y un nuevo código postal, el código postal estaba fuera de lugar. Dicho esto, fue la única fuente de datos que encontré que incluso tenía el nuevo código postal, tan pronto después de su creación.

Tenía una receta en mi libro para saber exactamente cómo cumplir con su solicitud ... en Drupal. Se basó en el módulo de herramientas de Google Maps ( enlace , que no debe confundirse con enlace , también un módulo digno.) Es posible que encuentre un código de muestra útil en esos módulos, aunque, por supuesto, no funcionarán de forma inmediata en WordPress.

    
respondido por el Marjorie Roswell 07.10.2010 - 22:27

Lea otras preguntas en las etiquetas