Add acars map page and show in-progress flights

This commit is contained in:
Nabeel Shahzad
2017-12-27 16:47:22 -06:00
parent a2e2a2a6a7
commit edca0644ff
103 changed files with 12380 additions and 827 deletions

View File

@@ -2,16 +2,15 @@
namespace App\Console\Commands;
use GuzzleHttp\Client;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Collection;
use GuzzleHttp\Client;
use App\Facades\Utils;
class AcarsReplay extends Command
{
protected $signature = 'phpvms:replay {files} {--manual} {--write-all}';
protected $signature = 'phpvms:replay {files} {--manual} {--write-all} {--no-submit}';
protected $description = 'Replay an ACARS file';
/**
@@ -64,11 +63,13 @@ class AcarsReplay extends Command
$pft = Utils::hoursToMinutes($flight->planned_hrsenroute,
$flight->planned_minenroute);
$flight_number = substr($flight->callsign, 3);
$response = $this->httpClient->post('/api/pirep/prefile', [
'json' => [
'airline_id' => 1,
'flight_number' => '6028',
'aircraft_id' => 1, # TODO: Lookup
'flight_number' => $flight_number,
'aircraft_id' => 1,
'dpt_airport_id' => $flight->planned_depairport,
'arr_airport_id' => $flight->planned_destairport,
'altitude' => $flight->planned_altitude,
@@ -84,6 +85,7 @@ class AcarsReplay extends Command
/**
* Mark the PIREP as filed
* @param $pirep_id
* @return mixed
*/
protected function filePirep($pirep_id)
{
@@ -187,8 +189,8 @@ class AcarsReplay extends Command
$this->postUpdate($pirep_id, $update);
# we're done
if($updates->count() === 0) {
# we're done and don't put the "no-submit" option
if($updates->count() === 0 && !$this->option('no-submit')) {
$this->filePirep($pirep_id);
}
})->filter(function ($updates, $idx) {

View File

@@ -56,13 +56,6 @@ class CreatePirepTables extends Migration
$table->timestamps();
});
Schema::create('pirep_events', function(Blueprint $table) {
$table->bigIncrements('id');
$table->string('pirep_id', 12);
$table->string('event', 64);
$table->dateTime('dt');
});
/*
* Financial tables/fields
*/

View File

@@ -3,41 +3,40 @@
namespace App\Http\Controllers\Api;
use Log;
use App\Models\Acars;
use Illuminate\Http\Request;
use App\Http\Controllers\AppBaseController;
use App\Models\Acars;
use App\Services\GeoService;
use App\Repositories\AcarsRepository;
use App\Repositories\PirepRepository;
use App\Http\Resources\Acars as AcarsResource;
class AcarsController extends AppBaseController
{
protected $acarsRepo, $pirepRepo;
protected $acarsRepo, $geoSvc, $pirepRepo;
public function __construct(
GeoService $geoSvc,
AcarsRepository $acarsRepo,
PirepRepository $pirepRepo
) {
$this->geoSvc = $geoSvc;
$this->acarsRepo = $acarsRepo;
$this->pirepRepo = $pirepRepo;
}
/**
* Return all of the flights (as points) in GeoJSON format
*/
public function index(Request $request)
{
/*PirepResource::withoutWrapping();
return new PirepResource($this->pirepRepo->find($id));*/
}
/**
* Return the current ACARS map data in GeoJSON format
* @param Request $request
*/
public function geojson(Request $request)
{
$pireps = $this->acarsRepo->getPositions();
$positions = $this->geoSvc->getFeatureForLiveFlights($pireps);
return response(json_encode($positions), 200, [
'Content-type' => 'application/json'
]);
}
}

View File

@@ -181,4 +181,13 @@ class PirepController extends AppBaseController
AcarsResource::withoutWrapping();
return new AcarsResource($update);
}
/**
* @param $id
* @param Request $request
*/
public function geojson($id, Request $request)
{
$pirep = $this->pirepRepo->find($id);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Http\Controllers\Frontend;
use App\Models\Pirep;
use App\Repositories\AcarsRepository;
use App\Services\GeoService;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class AcarsController extends Controller
{
private $acarsRepo, $geoSvc;
public function __construct(
AcarsRepository $acarsRepo,
GeoService $geoSvc
) {
$this->acarsRepo = $acarsRepo;
$this->geoSvc = $geoSvc;
}
/**
*
*/
public function index(Request $request)
{
$pireps = $this->acarsRepo->getPositions();
$positions = $this->geoSvc->getFeatureForLiveFlights($pireps);
return $this->view('acars.index',[
'pireps' => $pireps,
'positions' => $positions,
]);
}
}

View File

@@ -93,7 +93,8 @@ class Pirep extends BaseModel
public function acars()
{
return $this->hasMany('App\Models\Acars', 'pirep_id');
return $this->hasMany('App\Models\Acars', 'pirep_id')
->orderBy('created_at', 'desc');
}
public function aircraft()
@@ -118,12 +119,8 @@ class Pirep extends BaseModel
public function comments()
{
return $this->hasMany('App\Models\PirepComment', 'pirep_id');
}
public function events()
{
return $this->hasMany('App\Models\PirepEvent', 'pirep_id');
return $this->hasMany('App\Models\PirepComment', 'pirep_id')
->orderBy('created_at', 'desc');
}
public function fields()
@@ -141,14 +138,17 @@ class Pirep extends BaseModel
return $this->user();
}
public function route()
/**
* Relationship that holds the current position, but limits the ACARS
* relationship to only one row (the latest), to prevent an N+! problem
*/
public function position()
{
return [];
return $this->hasOne('App\Models\Acars', 'pirep_id')->latest();
}
public function user()
{
return $this->belongsTo('App\Models\User', 'user_id');
}
}

View File

@@ -1,45 +0,0 @@
<?php
namespace App\Models;
/**
* Class PirepEvent
*
* @package App\Models
*/
class PirepEvent extends BaseModel
{
public $table = 'pirep_fields';
public $fillable
= [
'name',
'value',
];
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'name' => 'string',
'required' => 'integer',
];
/**
* Validation rules
*
* @var array
*/
public static $rules
= [
'name' => 'required',
];
public function pirep()
{
return $this->belongsTo('App\Models\Pirep', 'pirep_id');
}
}

View File

@@ -66,10 +66,7 @@ class RouteServiceProvider extends ServiceProvider
protected function mapApiRoutes()
{
Route::group([
'middleware' => [
'api',
'api.auth',
],
'middleware' => ['api'],
'namespace' => $this->namespace."\\Api",
'prefix' => 'api',
'as' => 'api.',

View File

@@ -3,12 +3,16 @@
namespace App\Repositories;
use App\Models\Acars;
use App\Models\Pirep;
use App\Models\Enums\PirepState;
use App\Models\Enums\PirepStatus;
use App\Repositories\Traits\CacheableRepository;
use Prettus\Repository\Contracts\CacheableInterface;
class AcarsRepository extends BaseRepository implements CacheableInterface
class AcarsRepository extends BaseRepository //implements CacheableInterface
{
use CacheableRepository;
//use CacheableRepository;
public function model()
{
@@ -19,4 +23,30 @@ class AcarsRepository extends BaseRepository implements CacheableInterface
{
return $this->findWhere(['pirep_id' => $pirep_id]);
}
/**
* Get all of the PIREPS that are in-progress, and then
* get the latest update for those flights
* @return Pirep
*/
public function getPositions()
{
return Pirep::with(['airline', 'position'])
->where(['state' => PirepState::IN_PROGRESS])
->get();
/*return Pirep::with(['acars' => function($q) {
return $q->limit(1);
}])->where(['state' => PirepState::IN_PROGRESS])->get();*/
}
/**
* @return $this
*/
public function getAllAcarsPoints()
{
return Pirep::with('acars')->where([
'state' => PirepState::IN_PROGRESS
]);
}
}

View File

@@ -1,28 +1,28 @@
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::group([], function ()
/**
* public routes
*/
Route::group([], function()
{
Route::match(['get'], 'status', 'BaseController@status');
Route::match(['get'], 'airports/{id}', 'AirportController@get');
Route::match(['get'], 'airports/{id}/lookup', 'AirportController@lookup');
Route::match(['get'], 'acars', 'AcarsController@index');
Route::match(['get'], 'flights/search', 'FlightController@search');
Route::match(['get'], 'flights/{id}', 'FlightController@get');
Route::match(['post'], 'pirep/{id}/geojson', 'PirepController@file');
Route::match(['get'], 'status', 'BaseController@status');
});
/**
* these need to be authenticated with a user's API key
*/
Route::group(['middleware' => ['api.auth']], function ()
{
Route::match(['get'], 'airports/{id}', 'AirportController@get');
Route::match(['get'], 'airports/{id}/lookup', 'AirportController@lookup');
Route::match(['get'], 'pirep/{id}', 'PirepController@get');
Route::match(['post'], 'pirep/prefile', 'PirepController@prefile');
Route::match(['post'], 'pirep/{id}/file', 'PirepController@file');
@@ -30,9 +30,6 @@ Route::group([], function ()
Route::match(['get'], 'pirep/{id}/acars', 'PirepController@acars_get');
Route::match(['post'], 'pirep/{id}/acars', 'PirepController@acars_store');
Route::match(['get'], 'acars', 'AcarsController@index');
Route::match(['get'], 'acars/geojson', 'AcarsController@geojson');
# This is the info of the user whose token is in use
Route::match(['get'], 'user', 'UserController@index');
#Route::match(['get'], 'user/bids', 'UserController@index');

View File

@@ -8,8 +8,10 @@ Route::get('/', 'HomeController@index')->name('home');
Route::group([
'namespace' => 'Frontend', 'prefix' => '', 'as' => 'frontend.'
], function() {
Route::get('/r/{id}', 'PirepController@show')->name('pirep.show.public');
Route::get('/p/{id}', 'ProfileController@show')->name('profile.show.public');
Route::get('r/{id}', 'PirepController@show')->name('pirep.show.public');
Route::get('p/{id}', 'ProfileController@show')->name('profile.show.public');
Route::get('livemap', 'AcarsController@index')->name('livemap.public');
});
/**
@@ -22,7 +24,7 @@ Route::group([
Route::resource('dashboard', 'DashboardController');
Route::get('flights/search', 'FlightController@search')->name('flights.search');
Route::match(['post'], '/flights/save', 'FlightController@save')->name('flights.save');
Route::post('flights/save', 'FlightController@save')->name('flights.save');
Route::resource('flights', 'FlightController');
Route::resource('profile', 'ProfileController');

View File

@@ -139,6 +139,32 @@ class GeoService extends BaseService
return $coords;
}
/**
* Determine the center point between two sets of coordinates
* @param $latA
* @param $lonA
* @param $latB
* @param $lonB
* @return array
* @throws \League\Geotools\Exception\InvalidArgumentException
*/
public function getCenter($latA, $lonA, $latB, $lonB)
{
$geotools = new Geotools();
$coordA = new Coordinate([$latA, $lonA]);
$coordB = new Coordinate([$latB, $lonB]);
$vertex = $geotools->vertex()->setFrom($coordA)->setTo($coordB);
$middlePoint = $vertex->middle();
$center = [
$middlePoint->getLatitude(),
$middlePoint->getLongitude()
];
return $center;
}
/**
* Read an array/relationship of ACARS model points
* @param Pirep $pirep
@@ -171,12 +197,42 @@ class GeoService extends BaseService
# Convert to a feature
$route_line = new Feature(new LineString($route_line), [], 1);
# TODO: Draw the plane icon from the last point
return [
'line' => new FeatureCollection([$route_line]),
'points' => new FeatureCollection($route_points)
];
}
/**
* Return a single feature point for the
*/
public function getFeatureForLiveFlights($pireps)
{
$flight_points = [];
/**
* @var Pirep $pirep
*/
foreach($pireps as $pirep) {
/**
* @var $point \App\Models\Acars
*/
$point = $pirep->position;
$flight_points[] = new Feature(
new Point([$point->lon, $point->lat]), [
'gs' => $point->gs,
'alt' => $point->altitude,
'heading' => $point->heading ?: 0,
'popup' => 'Flight: ' . $pirep->ident,
]);
}
return new FeatureCollection($flight_points);
}
/**
* Return a FeatureCollection GeoJSON object
* @param Flight $flight
@@ -300,30 +356,4 @@ class GeoService extends BaseService
'actual_route_points' => $actual_route['points'],
];
}
/**
* Determine the center point between two sets of coordinates
* @param $latA
* @param $lonA
* @param $latB
* @param $lonB
* @return array
* @throws \League\Geotools\Exception\InvalidArgumentException
*/
public function getCenter($latA, $lonA, $latB, $lonB)
{
$geotools = new Geotools();
$coordA = new Coordinate([$latA, $lonA]);
$coordB = new Coordinate([$latB, $lonB]);
$vertex = $geotools->vertex()->setFrom($coordA)->setTo($coordB);
$middlePoint = $vertex->middle();
$center = [
$middlePoint->getLatitude(),
$middlePoint->getLongitude()
];
return $center;
}
}