MySQL выбирает почтовые индексы в пределах x км / миль в пределах y - PullRequest
2 голосов
/ 25 октября 2010

Примечание. Хотя я использую базу данных почтовых индексов с голландскими почтовыми индексами, этот вопрос не зависит от страны.

У меня есть база данных с каждым почтовым индексом в Нидерландах + его координаты x и y (широта / долгота).

У меня есть, например, почтовый индекс: $baseZipCode = 1044; со следующими координатами:

x coordinate = 4,808855
y coordinate = 52,406332

Теперь я хочу найти все другие почтовые индексы с $range из $baseZipCode.

Например:

SELECT
  zipcode
FROM
  zipcodes
WHERE
  ????? // Need help here

Проблема в том, что земля не совсем круглая. Я нашел много уроков по from a to b вычислениям, но это не то, что мне нужно.

У кого-нибудь есть идеи?


UPDATE Благодаря Captaintokyo я нашел это:

Хотите найти все почтовые индексы и соответствующие расстояния в пределах определенной мили / километра от другого почтового индекса или точки? Для решения этой проблемы требуются координаты широты и долготы. Геокодирование адреса дает вам координаты широты / долготы от адреса.

Сначала вам понадобится база данных всех почтовых индексов и соответствующих им координат широты и долготы:

CREATE TABLE `zipcodes` (
  `zipcode` varchar(5) NOT NULL DEFAULT '',
  `city` varchar(100) NOT NULL DEFAULT '',
  `state` char(2) NOT NULL DEFAULT '',
  `latitude` varchar(20) NOT NULL DEFAULT '',
  `longitude` varchar(20) NOT NULL DEFAULT '',
  KEY `zipcode` (`zipcode`),
  KEY `state` (`state`)
)

Таким образом, когда у вас есть база данных, вы хотите найти все почтовые индексы в пределах определенного радиуса мили от центральной точки. Если центральной точкой является другой почтовый индекс, просто запросите в базе данных координаты широты и долготы этого почтового индекса. Тогда код выглядит следующим образом:

// ITITIAL POINT

$coords = array('latitude' => "32.8", 'longitude' => "-117.17");

//RADIUS

$radius = 30;

// SQL FOR KILOMETERS

$sql = "SELECT zipcode, ( 6371 * acos( cos( radians( {$coords['latitude']} ) ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians( {$coords['longitude']} ) ) + sin( radians( {$coords['latitude']} ) ) * sin( radians( latitude ) ) ) ) AS distance FROM zipcodes HAVING distance <= {$radius} ORDER BY distance";

// SQL FOR MILES

$sql = "SELECT zipcode, ( 3959 * acos( cos( radians( {$coords['latitude']} ) ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians( {$coords['longitude']} ) ) + sin( radians( {$coords['latitude']} ) ) * sin( radians( latitude ) ) ) ) AS distance FROM zipcodes HAVING distance <= {$radius} ORDER BY distance";

// OUTPUT THE ZIPCODES AND DISTANCES

$query = mysql_query($sql);

while($row = mysql_fetch_assoc($query)){

    echo "{$row['zipcode']} ({$row['distance']})<br>\n";

}

(Yahoo и Google предлагают бесплатные услуги геокодирования.)

Ответы [ 3 ]

4 голосов
/ 25 октября 2010

Вы должны использовать нечто, называемое формулой Haversine :

$sql = "
    SELECT zipcode
    FROM zipcodes
    WHERE ".mysqlHaversine($lat, $lon, $distance)."
";

И формулой:

function mysqlHaversine($lat = 0, $lon = 0, $distance = 0)
{
    if($distance > 0)
    {
        return ('
        ((6372.797 * (2 *
        ATAN2(
            SQRT(
                SIN(('.($lat*1).' * (PI()/180)-latitude*(PI()/180))/2) *
                SIN(('.($lat*1).' * (PI()/180)-latitude*(PI()/180))/2) +
                COS(latitude * (PI()/180)) *
                COS('.($lat*1).' * (PI()/180)) *
                SIN(('.($lon*1).' * (PI()/180)-longitude*(PI()/180))/2) *
                SIN(('.($lon*1).' * (PI()/180)-longitude*(PI()/180))/2)
                ),
            SQRT(1-(
                SIN(('.($lat*1).' * (PI()/180)-latitude*(PI()/180))/2) *
                SIN(('.($lat*1).' * (PI()/180)-latitude*(PI()/180))/2) +
                COS(latitude * (PI()/180)) *
                COS('.($lat*1).' * (PI()/180)) *
                SIN(('.($lon*1).' * (PI()/180)-longitude*(PI()/180))/2) *
                SIN(('.($lon*1).' * (PI()/180)-longitude*(PI()/180))/2)
            ))
        )
        )) <= '.($distance/1000). ')');
    }

    return '';
}

Обычно я не использую код без пониманиякак это работает в первую очередь, но я должен признаться, эта функция немного над моей головой ...

2 голосов
/ 05 марта 2013

Хотя метод Captaintokyo точен, он также довольно медленный. Я не могу помочь, но думаю, что было бы более выгодно использовать временную таблицу всех почтовых индексов, границы которых находятся в пределах диапазона, а затем уточнить эти результаты по расстоянию.

0 голосов
/ 25 октября 2010

Вы хотите сделать что-то вроде этого:

SELECT zipcode FROM zipcodes WHERE DistanceFormula(lat, long, 4.808855, 52.406332) < $range

Это может быть медленно, если ваша таблица почтовых индексов большая. Вы также можете проверить геопространственные расширения для MySQL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...