2010-03-09 50 views
15

Sólo estoy en busca de sugerencias sobre la mejor manera de hacer esto ...PHP/MySQL de búsqueda de código postal proximidad

Necesito crear una función de búsqueda que busca "usuarios" dentro de un radio de 50 millas un código postal Tengo una tabla de código postal que contiene todos los códigos postales de EE. UU. Con su latitud/longitud, pero estoy tratando de descubrir la mejor manera de estructurar y consultar mis datos ...

Debo añadir columnas de latitud/longitud a la tabla de usuarios y consultarlo para todos los usuarios dentro del radio del código postal dado? ¿O debería consultar la tabla de códigos postales para todos los códigos postales que caen dentro del radio y luego consultar la tabla de usuarios para todos los usuarios con los resultados (códigos postales)? O ... ??? ¡Estoy abierto a cualquier sugerencia en este punto!

Gracias!

+0

¿Puede cambiar la marca de la respuesta aceptada? ¡Gracias! –

Respuesta

16

Aquí está la mejor manera que he encontrado. Por supuesto, requerirá que tenga todos sus códigos postales lat/lon codificados en la base de datos.

// get all the zipcodes within the specified radius - default 20 
function zipcodeRadius($lat, $lon, $radius) 
{ 
    $radius = $radius ? $radius : 20; 
    $sql = 'SELECT distinct(ZipCode) FROM zipcode WHERE (3958*3.1415926*sqrt((Latitude-'.$lat.')*(Latitude-'.$lat.') + cos(Latitude/57.29578)*cos('.$lat.'/57.29578)*(Longitude-'.$lon.')*(Longitude-'.$lon.'))/180) <= '.$radius.';'; 
    $result = $this->db->query($sql); 
    // get each result 
    $zipcodeList = array(); 
    while($row = $this->db->fetch_array($result)) 
    { 
     array_push($zipcodeList, $row['ZipCode']); 
    } 
    return $zipcodeList; 
} 

Debería poder simplemente omitir esta función. Pase los $ lat y $ lon del código postal para el que desea el radio, incluya el radio opcional y obtenga una lista de códigos postales.

Puede modificar esto fácilmente para que todos los usuarios obtengan el código postal IN (radius_sql) y recuperar su lista de usuarios.

Happy Coding!

+1

Esto sería genial, pero ¿cómo sugeriría ordenarlos por distancia? – mike

+1

Por lo tanto, la única forma en que podría hacerlo es comparar una dirección (lat/lon) con las direcciones circundantes (lat/lon). Dicho esto, aún puede solicitarlos por el código postal más cercano al más lejano. Pero tenga en cuenta que esto solo especificará el "área" del código postal, no necesariamente la proximidad a las otras direcciones. Yo diría que se agrega a la selección: (3958 * 3.1415926 * sqrt ((Latitude - '. $ Lat.') * (Latitude - '. $ Lat.') + Cos (Latitude/57.29578) * cos (' . $ lat. '/ 57.29578) * (Longitude -'. $ lon. ') * (Longitude -'. $ lon. '))/180) como LOC luego al final ORDER BY LOC. –

+0

Solo para que la gente sepa ... El Censo de los EE. UU. Tiene este recurso aquí: http://www.census.gov/tiger/tms/gazetteer/zips.txt – r109

2

Mira la búsqueda de proximidad ofrecido aquí:

Using PHP/MySQL with Google Maps

Si los datos están en la misma notación/proyección/formato (lo que se llama), es posible que funcione para usted.

+2

El enlace está muerto ... ¿Y qué ocurre si solo tengo códigos postales? No hay información de Long-Lati – Umair

+0

ERROR 404 - Actualice su respuesta. – Chinmay235

+0

@Chinu fair point - pero hay tantas otras buenas respuestas aquí, le pediré al OP que quite la marca de verificación para poder eliminar la respuesta –

0

El lat/long que tiene para cada código postal es un centro geográfico para ese zip, ¿no? Por lo tanto, si primero encuentra códigos postales con centros geográficos dentro de un radio de 50 millas, y luego los usuarios en esos códigos postales, podría estar devolviendo usuarios a más de 50 millas de distancia. Así que sacrificarías algo de precisión haciéndolo de esa manera.

Pero si tiene muchos usuarios (más que el número de códigos postales), esto sería más rápido, ya que primero consultaría contra la tabla de códigos postales más pequeños. Y podría indexar códigos postales en la tabla de usuarios, por lo que encontrar usuarios con un código postal en particular sería rápido.

Solo algunos pensamientos! Por lo tanto, si está esperando a muchos usuarios y el radio de 50 millas no necesita ser exacto, encontraría códigos postales dentro de 50 millas, luego los usuarios dentro de esos códigos postales.

3

http://www.micahcarrick.com/04-19-2005/php-zip-code-range-and-distance-calculation.html

Me pareció muy impresionante.

"consultar la tabla de códigos postales para todos los códigos postales que se encuentran dentro del radio a continuación, consulta la tabla de usuarios para todos los usuarios con los resultados (códigos postales)"

Me encontraron esta es la mejor manera de hacerlo a menos que necesites poner a los usuarios en un mapa de Google. Si solo está enumerando a los usuarios en el rango de millaje, debería ser muy fácil consultar la base de datos (usando la clase) para obtener una lista de cremalleras y luego seleccionar todos los usuarios en esos códigos postales.

Select * from Users where zip_code IN (19125,19081,19107.........); 

Eso debería hacerlo.

+0

Después de jugar un poco con las cosas. Creo que esta sería la mejor opción, pero no estoy seguro de cómo ordenaría los resultados finales (los usuarios) por distancia ... ¡y ese es el sitio del que obtuve mis datos del código postal! ;) – mike

+0

funcionó muy bien! gracias – ladieu

+0

Esto tiene un enlace muerto. – Ael

1

Consideraría reducir el número de candidatos con un cuadrado delimitador primero, y luego preocuparme por el radio como un segundo paso. Comience con las coordenadas del código postal, luego calcule el largo/lat de 50 millas en las 4 direcciones, luego seleccione solo los candidatos dentro de ese cuadro usando criterios simples de mayor/menor que. Si su base de usuarios está bien distribuida, esto reduce considerablemente el conjunto de candidatos, entonces solo tiene que hacer cálculos de distancia vectorial para eliminar las "esquinas".

2

de inicio aquí, pero tenga en cuenta que la solución no es muy rápido:

PHP Zip Code Range and Distance Calculation

ahora para hacer que sea rápido - que se va a sustituir a la consulta de utilizar un índice espacial :)

  1. uso de MySQL

  2. Añadir una columna a la base de datos llamada ubicación y hacen que sea el tipo de punto

  3. asegurarse de que acepta valores nulos en este momento

  4. ejecute la siguiente consulta SQL

    UPDATE zip_code SET location = PointFromText(CONCAT('POINT(',lon,' ',lat,')'));

  5. Ahora, hacen que la columna no acepta valores nulos

  6. Añadir un índice espacial para la columna de ubicación

  7. En el código del pro anterior Ject reemplazar la función 'get_zips_in_range' con lo siguiente:

    function get_zips_in_range($zip, $range, $sort=1, $include_base) 
        { 
    
    
        // returns an array of the zip codes within $range of $zip. Returns 
        // an array with keys as zip codes and values as the distance from 
        // the zipcode defined in $zip. 
    
        $this->chronometer();      // start the clock 
    
        $details = $this->get_zip_point($zip); // base zip details 
        if ($details == false) return false; 
    
        // This portion of the routine calculates the minimum and maximum lat and 
        // long within a given range. This portion of the code was written 
        // by Jeff Bearer (http://www.jeffbearer.com). This significanly decreases 
        // the time it takes to execute a query. My demo took 3.2 seconds in 
        // v1.0.0 and now executes in 0.4 seconds! Greate job Jeff! 
    
        // Find Max - Min Lat/Long for Radius and zero point and query 
        // only zips in that range. 
        $lat = $details[0]; 
        $lon = $details[1]; 
    
        $return = array(); // declared here for scope 
    
        $first = true; 
        $radius = $range/69.172; 
        $boundary = "POLYGON(("; 
        for($i=0; $i <= 360; $i += 360/24) 
        { 
         if($first) 
         { 
          $first = false; 
         } 
         else 
         { 
          $boundary .= ', '; 
         } 
    
         $clon = $radius*cos(deg2rad($i)) + $lon; 
         $clat = $radius*sin(deg2rad($i)) + $lat; 
         $boundary .= "$clon $clat" ; 
        } 
    
        $boundary .= '))'; 
    
        $sql = "SELECT zip_code, city, county, state_name, state_prefix, area_code, time_zone, lat, lon FROM zip_code WHERE MBRContains(GeomFromText('$boundary'), location);"; 
    
        //echo $sql; 
        $r = mysql_query($sql); 
    
        if (!$r) { // sql error 
    
         $this->last_error = mysql_error(); 
         return false; 
    
        } else { 
    
         while ($row = mysql_fetch_row($r)) { 
    
          // loop through the results to get the milage from src 
          $dist = $this->calculate_mileage($details[0],$row[7],$details[1],$row[8]); 
          if ($this->units == _UNIT_KILOMETERS) $dist = $dist * _M2KM_FACTOR; 
          $return[str_pad($row[0].', '.$row[1], 5, "0", STR_PAD_LEFT)] = round($dist, $this->decimals); 
    
         } 
         mysql_free_result($r); 
        } 
    
        // sort array 
        switch($sort) 
        { 
         case _ZIPS_SORT_BY_DISTANCE_ASC: 
          asort($return); 
          break; 
    
         case _ZIPS_SORT_BY_DISTANCE_DESC: 
          arsort($return); 
          break; 
    
         case _ZIPS_SORT_BY_ZIP_ASC: 
          ksort($return); 
          break; 
    
         case _ZIPS_SORT_BY_ZIP_DESC: 
          krsort($return); 
          break; 
        } 
    
        $this->last_time = $this->chronometer(); 
    
        if (empty($return)) return false; 
        return $return; 
        } 
    
+0

Al final, utilicé Sphinx y fue muy rápido ... ¡Gracias, sin embargo! – mike

1

Me gustaría primero hacer una búsqueda de todos los códigos postales en el radio del objetivo. Luego compare todos los códigos postales devueltos con los códigos postales de su tabla de usuario. Saca a los usuarios que coinciden.

Encuentra los códigos postales en un radio, que se encuentra esta llamada MySQL:

$query = 'SELECT zzip FROM ' . table . 
      ' WHERE (POW((69.1*(zlongitude-"' . 
      $long . '")*cos(' . $long . 
      '/57.3)),"2")+POW((69.1*(zlatitude-"' . 
      $lat . '")),"2"))<(' . $radius . 
      '*' . $radius . ')'; 

MySQL hace todo los cálculos por usted.

He encontrado una clase que utiliza esta aquí: http://www.nucleusdevelopment.com/code/do/zipcode

Espero que ayude.

+1

creo que 'cos ('. $ Long' en la tercera línea debería ser' cos ('. $ Lat' – yitwail

Cuestiones relacionadas