it-swarm-eu.dev

Jak mohu implementovat vyhledávání na umístění (PSČ) v aplikaci WordPress?

Pracuji na webu místního obchodního adresáře, který bude používat vlastní typy příspěvků pro obchodní položky. Jedním z polí bude „PSČ“. Jak mohu nastavit vyhledávání založené na poloze?

Chtěl bych, aby návštěvníci mohli zadat svůj PSČ a vybrat kategorii a ukázat všem podnikům s určitým poloměrem, nebo všechny podniky seřazené podle vzdálenosti. Viděl jsem pár pluginů, které tvrdí, že to dělají, ale nepodporují WordPress 3.0. Nějaké návrhy?

19
matt

/ Odpověď od gabrielku a odkazovaného blogu pomocí databázových indexů a minimalizace počtu skutečných výpočtů vzdálenosti .

Pokud znáte souřadnice uživatele a znáte maximální vzdálenost (řekněme 10 km), můžete nakreslit ohraničovací rámeček, který je 20 km po 20 km s aktuální polohou uprostřed. Získejte tyto ohraničující souřadnice a dotaz pouze ukládá mezi těmito zeměpisnými šířkami a délkami . V dotazu na databázi ještě nepoužívejte funkce trigonometrie, protože to zabrání použití indexů. (Takže můžete dostat obchod, který je od vás vzdálen 12 km, pokud se nachází v severovýchodním rohu ohraničovacího rámečku, ale v dalším kroku ho vyhodíme.)

Pro několik obchodů, které jsou vráceny, vypočítávejte vzdálenost (jak pták letí nebo se skutečnými směry jízdy, jak dáváte přednost). To bude výrazně zlepšit dobu zpracování, pokud máte velký počet obchodů.

Pro související vyhledávání ("dejte deset nejbližších obchodů") můžete udělat podobné vyhledávání, ale s počátečním odhadem vzdálenosti (takže začnete s oblastí 10 km po 10 km, a pokud nemáte dostatek obchodů rozbalíte na 20km 20km a tak dále). Pro tuto počáteční vzdálenost uhádněte, kolikrát jste v obchodě na celé ploše a využili to. Nebo zaznamenejte počet potřebných dotazů a časem je upravte.

Přidal jsem úplný kód příklad na Mike související otázku , a zde je rozšíření, které vám dává nejbližší X umístění (rychlé a sotva testován):

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();
10
Jan Fabry

Nejprve potřebujete tabulku, která vypadá takto:

Zip_code    lat     lon
10001       40.77    73.98

... naplněn pro každý PSČ. Můžete to rozšířit přidáním městských a státních polí, chcete-li se tímto způsobem podívat nahoru.

Pak může být každému obchodu přidělen Zip kód, a když potřebujete vypočítat vzdálenost, můžete se připojit k tabulce lat/long k datům úložiště.

Pak budete dotazovat tuto tabulku, abyste získali zeměpisnou šířku a délku pro PSČ a PSČ uživatele. Jakmile to dostanete, můžete naplnit své pole a předat jej funkci "dostat vzdálenost":

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

To je míněno jako důkaz o koncepci, nikoli kód, který bych ve skutečnosti doporučil. Pokud máte například 10.000 obchodů, bylo by to docela drahé operace dotazovat je všechny a smyčky a třídit je na každém požadavku.

8
gabrielk

Dokumentace MySQL obsahuje také informace o prostorových rozšířeních. Zvláště, standardní funkce () není dostupná, ale zkontrolujte tuto stránku: http://dev.mysql.com/tech-resources/articles/4.1/gis-with-mysql.html pro podrobnosti o jak "převést dvě hodnoty bodu na LINESTRING a pak spočítat jejich délku."

Všimněte si, že každý dodavatel pravděpodobně nabídne různé zeměpisné šířky a délky představující "těžiště" PSČ. Je také dobré vědět, že neexistují žádné reálné definované soubory typu „hranice“. Každý dodavatel bude mít svou vlastní sadu hranic, které zhruba odpovídají specifickým seznamům adres, které tvoří kód PSČ USPS. (Například v některých "hranicích" byste museli zahrnout obě strany ulice, v jiných pouze jednu.) Tabulkové oblasti PSČ (ZCTA), které dodavatelé široce používají, "přesně nezobrazují oblasti pro doručování PSČ, a nezahrnují všechny PSČ používané pro doručování pošty " http://www.census.gov/geo/www/cob/zt_metadata.html

Mnoho centrálních podniků bude mít svůj vlastní PSČ. Budete chtít jako kompletní soubor dat, jak je to možné, takže se ujistěte, že najdete seznam zipcodes, která obsahuje jak "bod" PSČ kódy (obvykle podniky), a "hranice" PSČ kódy.

Mám zkušenosti s práci s daty zipcode z http://www.semaphorecorp.com/ . Ani to nebylo 100% přesné. Například, když můj školní areál vzal novou poštovní adresu a nový PSČ, Zip byl nesprávně umístěn. To znamená, že to byl jediný zdroj dat, který jsem zjistil, že vůbec nový Zip kód vůbec, tak brzy poté, co byl vytvořen.

Ve své knize jsem měl recept na to, jak přesně splnit vaši žádost ... v Drupalu. Spoléhal na modul Nástroje pro Mapy Google ( http://drupal.org/project/gmaps , nesmí být zaměňován s http://drupal.org/project/gmap , ale také za hodný modul .) Můžete najít nějaký užitečný ukázkový kód v těchto modulech, i když, samozřejmě, nebudou fungovat z krabice ve WordPressu.

3
Marjorie Roswell