0

I am trying to get zipcodes within a radius of 160km of the provided latitude/longitude coordinates. I already have a database of zipcodes, latitudes & longitudes, my problem is calculate the radius.

I have been looking at examples of how to do this, and this is what I came up with. But evidently something is wrong, because I'm getting distances that are ~9000km away according to the $dist variable.

public function getClose($latitude, $longitude) {
    $latitudes  = [floor($latitude)-2, ceil($latitude)+2];
    $locations  = Zipcode::whereBetween('latitude', $latitudes)->get();

    foreach ($locations as $location) {
        $venueLat = $location->latitude;
        $venueLng = $location->longititude;

        $latDistance = deg2rad($latitude - $venueLat);
        $lngDistance = deg2rad($longitude - $venueLng);
        $a = (sin($latDistance / 2) * sin($latDistance / 2)) + (cos(deg2rad($latitude))) * (cos(deg2rad($venueLat))) * (sin($lngDistance / 2)) * (sin($lngDistance / 2));
        $c = 2 * atan2(sqrt($a), sqrt(1 - $a));

        // Distance between 2 points in km. 6371 = Earths radius
        $dist = 6371 * $c;
        var_dump($dist); // This is the line saying it's about 9000km away

        if ($dist < 160){
            var_dump($location->zip);
        }
    }
}

That code was mostly based on this answer on a related question: https://stackoverflow.com/a/18465217

But, obviously something didn't translate right, or I misunderstood something. Any help would be vastly appreciated!

Community
  • 1
  • 1
Kenyon
  • 807
  • 1
  • 12
  • 24
  • You should create a [SSCCE](http://sscce.org/) by singling out one pair of input location and venue location which exhibits this problem. If performance is an issue, you might want to filter using suitably chosen ranges for lat and long, so you have to do the trigonometry for only a few points. Or you omit the trigonometry altogether, and use [this approximation](http://stackoverflow.com/a/16271669/1468366) instead, computing distances in [equirectangular projection](http://en.wikipedia.org/wiki/Equirectangular_projection) and comparing their squared length against 160². – MvG Jul 08 '15 at 08:52
  • There wasn't really 1 location pair that was wrong, they were all off. I'll post the code I ended up using that worked. – Kenyon Jul 08 '15 at 16:29
  • Well, if *all* pairs are off, then *any one* would do. By the way, have you read [Wikipedia](https://en.wikipedia.org/wiki/Great-circle_distance#Computational_formulas)? in any case, posting the working solution sounds like a good idea. – MvG Jul 08 '15 at 16:33

1 Answers1

1

Here is the code that I ended up using that works. A couple things changed, but the biggest difference is the formula. As you can see they're significantly different. I'm not very good at math, so I don't really know what the differences are, but I do know this one works.

public function getClose($latitude, $longitude) {
    $latitudes  = [floor($latitude)-2, ceil($latitude)+2];
    $longitudes = [floor($longitude)-1, ceil($longitude)+1];
    $locations  = Zipcode::whereBetween('latitude', $latitudes)->whereBetween('longitude', $longitudes)->get();
    $zips       = [];

    foreach ($locations as $location) {
        $theta = $longitude - $location->longitude;
        $dist = sin(deg2rad($latitude)) * sin(deg2rad($location->latitude)) +  cos(deg2rad($latitude)) * cos(deg2rad($location->latitude)) * cos(deg2rad($theta));
        $dist = acos($dist);
        $dist = rad2deg($dist);
        $miles = $dist * 60 * 1.1515;

        if ($miles <= 100){
            $zips[] = $location->zip;
        }
    }
}
Kenyon
  • 807
  • 1
  • 12
  • 24