Home > Software engineering >  find nearest lat/lng - loop through subArray as $key
find nearest lat/lng - loop through subArray as $key

Time:03-01

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

CodePudding user response:

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

CodePudding user response:

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'];
  • Related