Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
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 surveying the Monastery: (9)
As of 2014-10-20 22:12 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    For retirement, I am banking on:










    Results (92 votes), past polls