Skip to content

Commit

Permalink
🐛 Fixed distance calculation for foreign trips (#247)
Browse files Browse the repository at this point in the history
* Improved distance calculation

* Renamed function

* Catched test cases

* Codacy

* Codacy
  • Loading branch information
MrKrisKrisu authored Apr 28, 2021
1 parent 5b0779a commit aa048f2
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 65 deletions.
116 changes: 116 additions & 0 deletions app/Http/Controllers/Backend/GeoController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php

namespace App\Http\Controllers\Backend;

use App\Http\Controllers\Controller;
use App\Models\HafasTrip;
use App\Models\TrainStopover;

class GeoController extends Controller
{
public static function calculateDistance(
HafasTrip $hafasTrip,
TrainStopover $origin,
TrainStopover $destination
): float|int {
if ($hafasTrip->polyline == null || $hafasTrip?->getPolyLine?->polyline == null) {
return self::calculateDistanceByStopovers($hafasTrip, $origin, $destination);
}

$allFeatures = json_decode($hafasTrip->getPolyLine->polyline);

$originIndex = null;
$destinationIndex = null;
foreach ($allFeatures->features as $key => $data) {
if (!isset($data->properties->id)) {
continue;
}
if ($origin->trainStation->ibnr == $data->properties->id && $originIndex == null) {
$originIndex = $key;
}
if ($destination->trainStation->ibnr == $data->properties->id) {
$destinationIndex = $key;
}
}

$slicedFeatures = array_slice($allFeatures->features, $originIndex, $destinationIndex - $originIndex + 1);

$distance = 0;
$lastStopover = null;
foreach ($slicedFeatures as $stopover) {
if ($lastStopover == null) {
$lastStopover = $stopover;
continue;
}

$distance += self::calculateDistanceBetweenCoordinates(
latitudeA: $lastStopover->geometry->coordinates[0],
longitudeA: $lastStopover->geometry->coordinates[1],
latitudeB: $stopover->geometry->coordinates[0],
longitudeB: $stopover->geometry->coordinates[1]
);

$lastStopover = $stopover;
}
return $distance;
}

/**
* Fallback calculation if no polyline is given. Calculates the length using the coordinates of the stations.
* @param HafasTrip $hafasTrip
* @param TrainStopover $origin
* @param TrainStopover $destination
* @return float
*/
private static function calculateDistanceByStopovers(
HafasTrip $hafasTrip,
TrainStopover $origin,
TrainStopover $destination
): float {
$stopovers = $hafasTrip->stopoversNEW->sortBy('departure');
$originStopoverIndex = $stopovers->search($origin);
$destinationStopoverIndex = $stopovers->search($destination);

$stopovers = $stopovers->slice($originStopoverIndex, $destinationStopoverIndex - $originStopoverIndex + 1);

$distance = 0;
$lastStopover = null;
foreach ($stopovers as $stopover) {
if ($lastStopover == null) {
$lastStopover = $stopover;
continue;
}
$distance += self::calculateDistanceBetweenCoordinates(
latitudeA: $lastStopover->trainStation->latitude,
longitudeA: $lastStopover->trainStation->longitude,
latitudeB: $stopover->trainStation->latitude,
longitudeB: $stopover->trainStation->longitude
);
}
return $distance;
}

private static function calculateDistanceBetweenCoordinates(
float $latitudeA,
float $longitudeA,
float $latitudeB,
float $longitudeB,
int $decimals = 3
): float {
if ($longitudeA === $longitudeB && $latitudeA === $latitudeB) {
return 0.0;
}

$equatorialRadiusInKilometers = 6378.137;

$pi = pi();
$latA = $latitudeA / 180 * $pi;
$lonA = $longitudeA / 180 * $pi;
$latB = $latitudeB / 180 * $pi;
$lonB = $longitudeB / 180 * $pi;
$distance = acos(sin($latA) * sin($latB) + cos($latA) * cos($latB) * cos($lonB - $lonA))
* $equatorialRadiusInKilometers;

return round($distance, $decimals);
}
}
42 changes: 0 additions & 42 deletions app/Http/Controllers/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace App\Http\Controllers;

use App\Models\PolyLine;
use Carbon\Carbon;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
Expand Down Expand Up @@ -36,45 +35,4 @@ public static function searchForId(
return null;
}

public static function polyline($idStart, $idStop, $hash) {
$polyline = PolyLine::where('hash', $hash)->first();
if ($polyline === null) {
return null;
}
$polyline = json_decode($polyline->polyline, true)['features'];
$offset = [];
foreach ($polyline as $key => $val) {
if (isset($val['properties']['id']) && $val['properties']['id'] === $idStart) {
$offset[0] = $key;
}
if (isset($val['properties']['id']) && $val['properties']['id'] === $idStop) {
$offset[1] = $key;
}
}
if ($offset[1] != key(array_slice($polyline, -1, 1, true))) {
$offset[1] = $offset[1] - count($polyline) + 1;
}

$polyline = array_slice($polyline, $offset[0], $offset[1]);

return $polyline;
}

public static function distanceCalculation($longitudeA, $latitudeA, $longitudeB, $latitudeB, $decimals = 3): float {
if ($longitudeA === $longitudeB && $latitudeA === $latitudeB) {
return 0.0;
}

$equatorialRadiusInKilometers = 6378.137;

$pi = pi();
$latA = $latitudeA / 180 * $pi;
$lonA = $longitudeA / 180 * $pi;
$latB = $latitudeB / 180 * $pi;
$lonB = $longitudeB / 180 * $pi;
$distance = acos(sin($latA) * sin($latB) + cos($latA) * cos($latB) * cos($lonB - $lonA))
* $equatorialRadiusInKilometers;

return round($distance, $decimals);
}
}
41 changes: 18 additions & 23 deletions app/Http/Controllers/TransportController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use App\Exceptions\CheckInCollisionException;
use App\Exceptions\HafasException;
use App\Exceptions\StationNotOnTripException;
use App\Http\Controllers\Backend\GeoController;
use App\Models\Event;
use App\Models\HafasTrip;
use App\Models\MastodonServer;
Expand Down Expand Up @@ -304,31 +305,10 @@ public static function TrainCheckin($tripId,
if ($offset1 === null || $offset2 === null) {
throw new StationNotOnTripException();
}
$polyline = self::polyline($start, $destination, $hafasTrip->polyline);

$originAttributes = $stopovers[$offset1];
$destinationAttributes = $stopovers[$offset2];

$distance = self::distanceCalculation($originAttributes['stop']['location']['latitude'],
$originAttributes['stop']['location']['longitude'],
$destinationAttributes['stop']['location']['latitude'],
$destinationAttributes['stop']['location']['longitude']);
if ($polyline !== null) {
$distance = 0.0;
foreach ($polyline as $key => $point) {
if ($key === 0) {
continue;
}
$distance += self::distanceCalculation(
$point['geometry']['coordinates'][0],
$point['geometry']['coordinates'][1],
$polyline[$key - 1]['geometry']['coordinates'][0],
$polyline[$key - 1]['geometry']['coordinates'][1]
);
// I really don't know what i did here or if there's a better version for this but fuck it,
// it's 5am and it works.
}
}

$originStation = self::getTrainStation(
$originAttributes['stop']['id'],
$originAttributes['stop']['name'],
Expand All @@ -341,7 +321,22 @@ public static function TrainCheckin($tripId,
$destinationAttributes['stop']['location']['latitude'],
$destinationAttributes['stop']['location']['longitude']
);
$points = self::CalculateTrainPoints(

$departureStopover = $hafasTrip->stopoversNEW
->where('train_station_id', $originStation->id)
->where('departure_planned', $departure)
->first();
$arrivalStopover = $hafasTrip->stopoversNEW
->where('train_station_id', $destinationStation->id)
->where('arrival_planned', $arrival)
->first();

$distance = 0;
if($departureStopover != null && $arrivalStopover != null) {
$distance = GeoController::calculateDistance($hafasTrip, $departureStopover, $arrivalStopover);
}

$points = self::CalculateTrainPoints(
$distance,
$hafasTrip->category,
$stopovers[$offset1]['departure'],
Expand Down

0 comments on commit aa048f2

Please sign in to comment.