skip to Main Content

as per the guide here
https://www.intelliwolf.com/find-nearest-location-from-array-of-coordinates-php/

I am trying to write a version that finds the nearest "ID" from the JSON feed found at https://easytide.admiralty.co.uk/Home/GetStations

So far I have constructed the following sandbox
https://3v4l.org/JX0i5#v7.3.1

<?PHP
// json will eventually be obtained via json_decode(file_get_contents('https://easytide.admiralty.co.uk/Home/GetStations')
$json = '
{
    "type": "FeatureCollection",
    "features": [{
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [
                -4.702540,
                51.672791
            ]
        },
        "properties": {
            "Id": "1603",
            "Name": "TENBY",
            "Country": "Channel Islands",
            "ContinuousHeightsAvailable": true
        }
    }, {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [
          -1.878530,
          50.721680
        ]
      },
      "properties": {
        "Id": "1603A",
        "Name": "Bournmouth",
        "Country": "Channel Islands",
        "ContinuousHeightsAvailable": true
      }
    }, {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [
          -2.35,
          49.433333
        ]
      },
      "properties": {
        "Id": "1603A",
        "Name": "Maseline Pier",
        "Country": "Channel Islands",
        "ContinuousHeightsAvailable": true
      }
    }]
}';


$locations = json_decode($json, true);
#print_r($locations); //check json decoded

// set the base location will eventually be passed via $_GET
$base_location = array(
  'lat' => "51.672791",  
  'lng' => "-4.702540"
);

//create the array for recording the calculated distances
$distances = array();

//sure this count is not needed but used it to check the array was being read
for ($c = 0; $c < count($locations["features"]); $c++) {

    // this is where I go wrong 
    foreach($locations as $i){
        foreach ($i as $z) { 
            foreach ($z['geometry'] as $key => $location) {
                if ($key == "coordinates"){
                    #    print_r($z);
                    //work out the distance Pythagorean Theorem calculating how far it is from $base_location is
                    $a = $base_location['lat'] - $location[1];
                    $b = $base_location['lng'] - $location[0];
                    $distance = sqrt(($a**2) + ($b**2));
                    $distances[$c] = $distance;
                    
                    //nonsense used to test I was getting the correct output
                    echo "The value of key '$key' is '$location'", PHP_EOL;
                }
            }
        }
    }
}
#print_r($base_location);

//sort distances to get the closest first 
asort($distances);
print_r($distances);

//not really got to this part yet but want to return the "properties=>ID"
$closest = $location[key($distances)];
#print_r($closest);
#echo "<br>Closest port from ARRAY is: " . $closest['features'][1]['properties']['Id'];

I would love for someone to explain in simple terms how I get this to work:-

  1. search the array found at https://easytide.admiralty.co.uk/Home/GetStations
  2. find the nearest location based on the lat/long provided
  3. print the "properties=>ID" from the corresponding array (to be used later in another file_get_contents request)

I have poured over it for many hours and have posted other versions of this question which were automatically closed.

I want to create this as a learning project but am stuck on this element. If this is not the place to ask for this type of help, please advise where is.

Many thanks

2

Answers


  1. Chosen as BEST ANSWER

    I managed to work out a 'solution' but perhaps not the most graceful?

    foreach($locations as $i){
        if (is_array($i) || is_object($i)) {
            foreach ($i as $z) { 
                foreach ($z['geometry'] as $key => $location) {
                    if ($key == "coordinates"){
                         #  print_r($z);
                         $a = $base_location['lat'] - $location[1];
                         $b = $base_location['lng'] - $location[0];
                         $distance = sqrt(($a**2) + ($b**2));
                         $distances[$c++] = $distance;
                         # echo "The value of key '$key' is '$location'", PHP_EOL;
                        }
                    }
                }
            }
        }
    
    asort($distances);
    $closest = $i[key($distances)];
    print_r($closest); 
    echo "Closest Id: " . $closest['properties']['Id']. " name: " . $closest['properties']['Name'];
    

  2. This seemed like an interesting exercise, a bit of a cast around and all the code you need is out there for you to utilise

    function getDistance($from_lat, $from_long, $to_lat, $to_long, $unit = 'nmi', $decimals = 2) {
        // Calculate the distance in degrees
        $degrees = rad2deg(acos((sin(deg2rad($from_lat))*sin(deg2rad($to_lat))) + (cos(deg2rad($from_lat))*cos(deg2rad($to_lat))*cos(deg2rad($from_long-$to_long)))));
    
        // Convert the distance in degrees to the chosen unit (kilometres, miles or nautical miles)
        switch($unit) {
            case 'km':
                $distance = $degrees * 111.13384; // 1 degree = 111.13384 km, based on the average diameter of the Earth (12,735 km)
                break;
            case 'mi':
                $distance = $degrees * 69.05482; // 1 degree = 69.05482 miles, based on the average diameter of the Earth (7,913.1 miles)
                break;
            case 'nmi':
                $distance =  $degrees * 59.97662; // 1 degree = 59.97662 nautic miles, based on the average diameter of the Earth (6,876.3 nautical miles)
        }
        return round($distance, $decimals);
    }
    
    $json_stations = file_get_contents('https://easytide.admiralty.co.uk/Home/GetStations');
    $stations = json_decode($json_stations);
    
    $i_am_here = ['lat' => 50.77842324616663,  'lng' => -1.087804949548603];    // Castle Field Portsmouth
    
    $closest = [];       // index into stations
    $distance = null;
    foreach ($stations->features as $index => $feature){
        $lng = $feature->geometry->coordinates[0];
        $lat = $feature->geometry->coordinates[1];
        
        $dist = getDistance($i_am_here['lat'], $i_am_here['lng'], $lat, $lng, $unit = 'nmi', $decimals = 4);
        if ( $distance == NULL || $dist < $distance ){
            $distance = $dist;
            $closest = ['distance' => $dist, 'index' => $index, 'properties' => $feature->properties];
        }
    }
    
    print_r($closest);
    echo 'Distance = ' . $distance;
    

    RESULTS

    Array
    (
        [distance] => 1.6947
        [index] => 85
        [properties] => stdClass Object
            (
                [Id] => 0065
                [Name] => PORTSMOUTH
                [Country] => England
                [ContinuousHeightsAvailable] => 1
            )
    
    )
    Distance = 1.6947
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search