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:-
- search the array found at https://easytide.admiralty.co.uk/Home/GetStations
- find the nearest location based on the lat/long provided
- 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'];