Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

Getting Locations using Distance

by Anonymous Monk
on Apr 12, 2006 at 17:14 UTC ( #542896=perlquestion: print w/ replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi, Monks!
I would like to ask a question if anyone would know a formula to get all locations, ina a radius, if a starting Zip Code and Distance in Miles or Kilometers was given?
The code I have here give the distance between, but what I am looking for is to get all the location near a Zip Code and distance given.
Here is the code

This is the HTML form:
<FORM METHOD="post" ACTION="../cgi-bin/findzip.cgi"> Enter zips:<br> <table> <tr><td>First Zip Code</td><td><input name="zip1"></td></tr> <tr><td>Second Zip Code</td><td><input name="zip2"></td></tr> </table> <INPUT TYPE=submit VALUE="Look up Zips and distances"> </FORM>

Here is actually the HTML form that I want to use:
<FORM METHOD="post" ACTION="../cgi-bin/findzip.cgi"> <input type="hidden" name="zip1" value="02110"> <table width="300" border="0" align="center" cellpadding="4" cel +lspacing="0" bgcolor="#FBFBFB"> <tr align="center"> <td colspan="2"><b>Zip Radius Search</b></td> </tr> <tr> <td align="right">Zip code:</td> <td><input type="text" name="zip2" size="5" maxlength="5"></ +td> </tr> <tr> <td align="right">Distance:</td> <td> <select name="distance"> <option>2</option> <option>5</option> <option>8</option> <option>10</option> </select> &nbsp; &nbsp; <select name="unit"> <option value='0'>Miles</option> <option value='1'>KM</option> </select> </td> </tr> <tr> <td></td> <td><input type="submit" name="Submit" value="Show all citie +s"></td> </tr> </table> </form>


The perl code:
#!/perl/bin/perl #use strict; print "Content-type: text/html\n\n"; print "<h1>Zip Code Lookup Results</h1>\n"; $debug=0; if ($ENV{'CONTENT_LENGTH'} == 0) { $ENV{'CONTENT_LENGTH'} = 62; $debug = 1; } print "L15 - $ENV{'CONTENT_LENGTH'}<br>\n"; $argcnt=0; read(STDIN, $input, $ENV{'CONTENT_LENGTH'}); if ($debug) { print "Input string:$input <br>"; print "L23 - $ENV{'CONTENT_LENGTH'}<br>\n"; } @input = split(/&/,$input); PARSE: for ($i=0;$i<=$#input;$i++){ ($g, $z) = split(/=/, $input[$i],2); # $z =~ s/ //g; last PARSE if ($z==""); $ARGV[$i] = $z; $argcnt++; } $argcnt--; #print "LINE 35 - argcnt=$argcnt<br>"; # $zlist = split(/=/,@input); $stime=time(); @ziplist = ($z1, $z2); print "L44 - $ziplist[0] $ziplist[1] <br><hr>"; $stime=time(); #$ARGV[0]=$z1; #$ARGV[1]=$z2; for ($i=0; $i<= $argcnt;$i++){ #print "In find loop: $i\n"; ($zip[$i], $state[$i], $city[$i], $lat[$i], $long[$i]) = findzip($ +ARGV[$i]); $city[$i] =~ s/^ +//g; #match one or more spaces at the start of + a line. # print "$zip[$i], $city[$i], $state[$i], $lat[$i], $long[$i]\n<br>" +; if ($lat[$i] == 0 || $long[$i] == 0){ print "Zip $zip[$i] is missing location information<br>\n"; } } for ($i=0; $i <= $argcnt; $i++){ $dest=$i+1; if ($i == $argcnt) {$dest=0;} $dst[$i] = dist($lat[$i], $long[$i], $lat[$dest], $long[$dest] +); if ($lat[$i] == 0 || $long[$i] == 0 || $lat[$dest] == 0 || $long[$dest] == 0) { $dst[$i] = "Unknown distance"; } else { #print "$dst[$i]\n"; $dst[$i] =~ s/\..+/ miles/; #print "$dst[$i]\n"; } # print "$city[$i], $state[$i] $zip[$i] $lat[$i] $long[$i] to #$city[$dest], $state[$dest] $zip[$dest] $lat[$dest] $long[$dest] :"; print "L 85 - $city[$i], $state[$i] $zip[$i] to $city[$dest], $state[$dest] $zip[$dest] : "; print "<b>$dst[$i] (lat $lat[$i] long $long[$i])</b>****$dst[$i +]****<br>"; } print "L90 - <HR>\n"; $etime=time(); elapsed() ; print "<a href=\"../../zip/zipmain.html\">Return to Zip Code Lookup</a +><br>\n"; #print "<a href=\"/index.htm\">Go to ZIP!</a><br>\n"; exit; $linecnt=1; #loop counter, how many have we looked at? $fnd=-1; #how many have we found? SEARCH: while ($line = <ZIP>) { $thiszip = substr($line, 0, 5); $i=0; while ($ARGV[$i]){ if ($thiszip == $ARGV[$i]){ $short = substr($line, 0, 50); print "line #: $linecnt Found Zip: $short"; chomp $line; ($zip[$i], $state[$i], $name[$i], $lat[$i], $long[$i]) = split /,/,$line,5; @fullline[$i] = $line; $fnd++; last SEARCH if($fnd==$#ARGV) ; } $i++; } $linecnt++; } print "\n\n"; $i=0; for ($i=0; $i <= $#ARGV; $i++){ $dest=$i+1; if ($i == $#ARGV) {$dest=0;} $dst[$i] = dist($lat[$i], $long[$i], $lat[$dest], $long[$dest] +); print "$name[$i], $state[$i] to $name[$dest], $state[$dest]: $ +dst[$i] miles \n"; } exit; sub elapsed { # etime set after search. $etime=time(); #print "-------------------------------------------------------------- +-\n"; #print "start = $stime end = $etime<br> \n"; print "Elapsed time = "; print $etime - $stime; print "<br><br><br>\n"; } sub acos { atan2( sqrt(1-$_[0] * $_[0]), $_[0]) } sub dist { $pi = atan2(1,1) * 4; my @parms = @_; my $lat1 = $parms[0]/180 * $pi ; my $long1 = $parms[1]/180 * $pi; my $lat2 = $parms[2]/180 * $pi; my $long2 = $parms[3]/180 * $pi; # print "$lat1, $long1, $lat2, $long2 \n"; $a = $long1 - $long2; if ($a < 0) {$a = -$a;} if ($a > $pi) {$a = 2 * $pi;} $d = acos(sin($lat2) * sin($lat1) + cos($lat2)*cos($lat1)*cos($a)) + * 3958; return $d ; } ###################################################################### +### #This findzip is awesome...uses an index, and works okay. It is fast +and #cool. Next issue: Can we do without the index? sub findzipX { my @parms = @_; my $z = $parms[0]; open(ZIP, "zip2.txt") ; open(NDX, "zip.ndx") ; $keysize=11; #how long is the key $zipsize=52; #how long is a zip code record $zipcnt=42730; #how many zip codes are in file $max = $zipcnt; $min = 0; $cand=""; $itcnt=0; while ($cand != $z and itcnt < 100){ #set a search value #read it #key is either greater, lesser, or equal $search=int($min + (($max-$min)/2)); seek NDX, $search*$keysize, 0; read NDX, $line,$keysize; ($cand, $key) = split /:/,$line ; if ($cand > $z) { $max= $search; #$min= $min; } elsif ($cand < $z){ #$max= $max; $min= $search; } print "Cnt=$itcnt zip=$cand key=$key search=$search max=$max m +in=$min\n"; $itcnt++; } # print "Cnt=$itcnt zip=$cand key=$key search=$search max=$max min= +$min\n"; # print "\n"; # print "key=$key\n"; seek ZIP, ($key*$zipsize), 0; read ZIP, $line, $zipsize; # print "In findzip $z=$line\n"; return split /,/,$line; } ###################################################################### +#### sub test { return "Test1","test2"; } ###################################################################### +### #this is a copy of the original findzip (now findzipx)...I am trying t +o do #without the index file. sub findzip { my @parms = @_; my $z = $parms[0]; #print "Looking for $z\n"; # open(ZIP, "zip2.txt") ; # open(NDX, "zip.ndx") ; #now try the 'normal' zip.txt file, no indexing, no optimization. open(ZIP, "zip.txt"); $keysize=11; #how long is the key $zipsize=52; #how long is a zip code record $zipcnt=42730; #how many zip codes are in file #the technique is similar... $zipsize = 1698323; # of bytes in zip.txt #everything now refers to bytes, rather than records $max = $zipsize; $min = 0; $cand=""; $itcnt=0; $search=int($min + (($max-$min)/2)); # print "Cnt=$itcnt zip=$cand key=$key search=$search max=$maxmin=$ +min\n"; while ($cand != $z){ #set a search value #read it #key is either greater, lesser, or equal $search=int($min + (($max-$min)/2)); if ($itcnt > 100) { @alist = ($z,"Not Found","Not Found","",""); @alist; return; } #print "In loop search $search max $max min $min itcnt $itcnt\n"; seek ZIP, $search, 0; # read NDX, $line,$keysize; #get rid of a partial line because we probably seek'ed into th +e middle of a record $line = <ZIP> ; #partial line $line = <ZIP> ; #read a full line ($cand, $key) = split /:/,$line,2 ; #just first two fields if ($cand > $z) { $max= $search + 100; #plus 100, just to make sure we don't l +ose a line # $min= $min; } elsif ($cand<$z){ # $max= $max; $min= $search - 100; } # print "Cnt=$itcnt zip=$cand key=$key search=$search max=$max #min=$min\n"; $itcnt++; } # print "Cnt=$itcnt zip=$cand key=$key search=$search max=$max min= +$min\n"; # print "\n"; # print "key=$key\n"; #--- seek ZIP, ($key*$zipsize), 0; #--- read ZIP, $line, $zipsize; # print "In findzip $z=$line\n"; return split /,/,$line; }

Data Sample (It will be to big to have all the data for the zip locations, but here is some:
00401,NY,Pleasantville,41.075800,-73.47300 00501,NY,Holtsville,40.485500,-73.02400 00544,NY,Holtsville,40.485500,-73.02400 00601,PR,Adjuntas,18.095300,-66.43200 00602,PR,Aguada,18.225300,-67.11100 00603,PR,Aguadilla,18.254600,-67.09100 00604,PR,Aguadilla,18.254600,-67.09100 00605,PR,Aguadilla,18.254600,-67.09100 00607,PR,Aguas Buenas,18.153200,-66.06100 00608,PR,Aguirre,0.000000,0.00000 00609,PR,Aibonito,18.083100,-66.15500 00610,PR,Anasco,18.170500,-67.08200 00611,PR,Angeles,18.171300,-66.47500 00612,PR,Arecibo,18.282800,-66.42500 00613,PR,Arecibo,18.282800,-66.42500 00615,PR,Arroyo,17.580400,-66.03400 00616,PR,Bajadero,18.254300,-66.41000 00617,PR,Barceloneta,18.270900,-66.32200 00618,PR,Barranquitas,18.111900,-66.18200 00619,PR,Bayamon,18.240200,-66.09200 00620,PR,San Juan,18.280600,-66.06200 00621,PR,Bayamon,18.240200,-66.09200 00622,PR,Boqueron,18.035700,-66.30100 00623,PR,Cabo Rojo,18.051900,-67.08400 00625,PR,Caguas,18.141000,-66.02500 00626,PR,Caguas,18.141000,-66.02500 00627,PR,Camuy,18.290900,-66.50400 00628,PR,Carolina,18.225800,-65.57200


Thanks for the Help!!!

Comment on Getting Locations using Distance
Select or Download Code
Re: Getting Locations using Distance
by kvale (Monsignor) on Apr 12, 2006 at 17:28 UTC
    The easiest approach is to traverse your list of locations and compute the distaance of each location from your designated position. To get locations of zip codes, try Geo::Coder::US:
    use Geo::Coder::US; Geo::Coder::US->set_db( "geocoder.db" ); my ($ora) = Geo::Coder::US->geocode( "1005 Gravenstein Hwy N, 95472" ); print "O'Reilly is located at $ora->{lat} degrees north, " "$ora->{long} degrees east.\n";
    To compute distances, use Geo::Ellipsoid:
    use Geo::Ellipsoid; $geo = Geo::Ellipsoid->new(ellipsoid=>'NAD27', units=>'degrees'); @origin = ( 37.619002, -122.374843 ); # SFO @dest = ( 33.942536, -118.408074 ); # LAX ( $range, $bearing ) = $geo->to( @origin, @dest ); ($lat,$lon) = $geo->at( @origin, 45.0, 2000 ); ( $x, $y ) = $geo->displacement( @origin, $lat, $lon ); @pos = $geo->location( $lat, $lon, $x, $y );

    -Mark

Re: Getting Locations using Distance
by sgifford (Prior) on Apr 12, 2006 at 17:34 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://542896]
Approved by ikegami
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (15)
As of 2014-07-23 20:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (152 votes), past polls