Comment puis-je implémenter une recherche basée sur l'emplacement (code postal) dans WordPress?

19

Je travaille sur un site de répertoire professionnel local qui utilisera des types de publication personnalisés pour les entrées professionnelles. L'un des champs sera "Code postal". Comment puis-je configurer une recherche par emplacement?

Je souhaite que les visiteurs puissent entrer leur code postal, choisir une catégorie et afficher toutes les entreprises d’un certain rayon ou toutes les entreprises classées par distance. J'ai vu quelques plugins qui prétendent le faire, mais ils ne supportent pas WordPress 3.0. Des suggestions?

    
posée matt 28.09.2010 - 00:32

3 réponses

10

Je modifierais la réponse de gabrielk et l'article de blog lié en utilisant les index de base de données et le nombre de calculs de distance réels .

Si vous connaissez les coordonnées de l'utilisateur et la distance maximale (par exemple, 10 km), vous pouvez dessiner un cadre de sélection de 20 km sur 20 km avec la position actuelle au milieu. Obtenez ces coordonnées de délimitation et la requête stocke uniquement entre ces latitudes et longitudes . N'utilisez pas encore de fonctions trigonométriques dans votre requête de base de données, car cela empêcherait l'utilisation des index. (Vous pouvez donc vous procurer un magasin situé à 12 km de distance s’il se trouve dans le coin nord-est du cadre de sélection, mais nous le jetterons à l’étape suivante.)

Calculez uniquement la distance (à vol d'oiseau ou avec les directions réelles, selon vos préférences) pour les quelques magasins restitués. Cela améliorera considérablement le temps de traitement si vous avez un grand nombre de magasins.

Pour la recherche associée ( "donner les dix magasins les plus proches" ), vous pouvez effectuer une recherche similaire, mais avec une estimation de distance initiale (vous commencez donc par une zone de 10 km sur 10 km). Si vous n’avez pas assez de magasins, vous l’étendez à 20 km sur 20). Pour cette distance initiale, calculez une fois le nombre de magasins sur la surface totale et utilisez-le. Ou enregistrez le nombre de requêtes nécessaires et adaptez-vous au fil du temps.

J'ai ajouté un Exemple de code complet à la question connexe de Mike , et voici une extension qui vous donne les emplacements X les plus proches (rapide et à peine testé):

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();
    
réponse donnée Jan Fabry 07.10.2010 - 11:52
8

Vous avez d’abord besoin d’une table ressemblant à ceci:

zip_code    lat     lon
10001       40.77    73.98

... rempli pour chaque code postal. Vous pouvez développer ceci en ajoutant des champs de ville et d'état si vous voulez regarder de cette façon.

Ensuite, vous pouvez attribuer un code postal à chaque magasin. Lorsque vous devez calculer la distance, vous pouvez joindre la table lat / long aux données du magasin.

Vous allez ensuite interroger cette table pour obtenir la latitude et la longitude du magasin et les codes postaux de l'utilisateur. Une fois que vous l'obtenez, vous pouvez remplir votre tableau et le transmettre à une fonction "obtenir une distance":

$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);
}

Ceci est conçu comme une preuve de concept et non comme un code que je recommanderais réellement de mettre en œuvre. Si vous avez 10 000 magasins, par exemple, il serait assez coûteux de les interroger tous, de les parcourir en boucle et de les trier à chaque demande.

    
réponse donnée gabrielk 05.10.2010 - 00:04
3

La documentation MySQL comprend également des informations sur les extensions spatiales. Bizarrement, la fonction distance standard () n'est pas disponible, mais vérifiez cette page: enlace pour plus de détails sur la façon de "convertir les deux valeurs POINT en LINESTRING, puis en calculer la longueur."

Notez que chaque fournisseur est susceptible d’offrir des latitudes et longitudes différentes représentant le "centroïde" d’un code postal. Cela vaut également la peine de savoir qu’il n’existe pas de fichiers "limites" de code postal bien définis. Chaque fournisseur aura son propre ensemble de limites qui correspond approximativement aux listes d'adresses spécifiques qui constituent un code postal USPS. (Par exemple, dans certaines "limites", vous devez inclure les deux côtés de la rue, dans d'autres, un seul.) Les zones de tabulation de code postal (ZCTA), largement utilisées par les fournisseurs, "ne décrivent pas avec précision les zones de livraison du code postal, et n'inclut pas tous les codes postaux utilisés pour la distribution du courrier " enlace

De nombreuses entreprises du centre-ville auront leur propre code postal. Vous souhaiterez un ensemble de données aussi complet que possible. Assurez-vous donc de trouver une liste de codes postaux comprenant des codes postaux "ponctuels" (généralement des entreprises) et des codes postaux "limites".

J'ai de l'expérience dans l'utilisation des données de code postal de enlace . Même cela n'était pas précis à 100%. Par exemple, lorsque mon campus a pris une nouvelle adresse postale et un nouveau code postal, celui-ci a été égaré. Cela dit, c’est la seule source de données à laquelle j’ai découvert que même le nouveau code postal était ADAPTÉ, peu de temps après sa création.

J'avais une recette dans mon livre pour savoir exactement comment répondre à votre demande ... dans Drupal. Il s’appuyait sur le module Google Maps Tools ( enlace ), à ne pas confondre avec enlace , est également un module utile.) Vous trouverez peut-être des exemples de code utiles dans ces modules, bien que, évidemment, ils ne fonctionneront pas tels quels dans WordPress.

    
réponse donnée Marjorie Roswell 07.10.2010 - 22:27

Lire d'autres questions sur les étiquettes