From e5278bc0bfe86affd985579f0eb9e3d363fe6fd6 Mon Sep 17 00:00:00 2001 From: Nabeel Shahzad Date: Wed, 20 Dec 2017 11:28:21 -0600 Subject: [PATCH] Output PIREP map with just the dep/arr airports for now --- .../Controllers/Frontend/PirepController.php | 13 + app/Models/Airport.php | 14 +- app/Models/Pirep.php | 5 + app/Services/GeoService.php | 72 +++ app/Services/PIREPService.php | 2 +- composer.lock | 169 ++++-- public/assets/system/js/system.js | 44 +- .../assets/vendor/leaflet/leaflet.geodesic.js | 547 ++++++++++++++++++ resources/views/layouts/default/app.blade.php | 5 +- .../layouts/default/pireps/map.blade.php | 10 +- 10 files changed, 812 insertions(+), 69 deletions(-) create mode 100644 app/Services/GeoService.php create mode 100644 public/assets/vendor/leaflet/leaflet.geodesic.js diff --git a/app/Http/Controllers/Frontend/PirepController.php b/app/Http/Controllers/Frontend/PirepController.php index eba2230b..c0b13168 100644 --- a/app/Http/Controllers/Frontend/PirepController.php +++ b/app/Http/Controllers/Frontend/PirepController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Frontend; use App\Facades\Utils; use App\Models\Enums\PirepSource; use App\Repositories\Criteria\WhereCriteria; +use App\Services\GeoService; use App\Services\PIREPService; use Illuminate\Support\Facades\Auth; use Illuminate\Http\Request; @@ -27,6 +28,7 @@ class PirepController extends Controller $pirepRepo, $airportRepo, $pirepFieldRepo, + $geoSvc, $pirepSvc; public function __construct( @@ -35,6 +37,7 @@ class PirepController extends Controller AircraftRepository $aircraftRepo, AirportRepository $airportRepo, PirepFieldRepository $pirepFieldRepo, + GeoService $geoSvc, PIREPService $pirepSvc ) { $this->airlineRepo = $airlineRepo; @@ -42,6 +45,8 @@ class PirepController extends Controller $this->pirepRepo = $pirepRepo; $this->airportRepo = $airportRepo; $this->pirepFieldRepo = $pirepFieldRepo; + + $this->geoSvc = $geoSvc; $this->pirepSvc = $pirepSvc; } @@ -110,8 +115,16 @@ class PirepController extends Controller { #$pirep = Pirep::where('id', $id); $pirep = $this->pirepRepo->find($id); + if (empty($pirep)) { + Flash::error('Pirep not found'); + return redirect(route('frontend.pirep.index')); + } + + $coords = $this->geoSvc->getRouteCoordsGeoJSON($pirep); + return $this->view('pireps.show', [ 'pirep' => $pirep, + 'coords' => $coords, ]); } } diff --git a/app/Models/Airport.php b/app/Models/Airport.php index 98f5944e..c5c603fb 100644 --- a/app/Models/Airport.php +++ b/app/Models/Airport.php @@ -30,6 +30,7 @@ class Airport extends Model protected $casts = [ 'id' => 'string', ]; + /** * Validation rules * @@ -42,7 +43,8 @@ class Airport extends Model /** * Some fancy callbacks */ - protected static function boot() { + protected static function boot() + { parent::boot(); @@ -54,4 +56,14 @@ class Airport extends Model $model->id = $model->icao; }); } + + /** + * Return full name like: + * KJFK - John F Kennedy + * @return string + */ + public function getFullNameAttribute(): string + { + return $this->icao . ' - ' . $this->name; + } } diff --git a/app/Models/Pirep.php b/app/Models/Pirep.php index 6682ebb8..8ea42559 100644 --- a/app/Models/Pirep.php +++ b/app/Models/Pirep.php @@ -131,6 +131,11 @@ class Pirep extends Model return $this->user(); } + public function route() + { + return []; + } + public function user() { return $this->belongsTo('App\Models\User', 'user_id'); diff --git a/app/Services/GeoService.php b/app/Services/GeoService.php new file mode 100644 index 00000000..1fd8475b --- /dev/null +++ b/app/Services/GeoService.php @@ -0,0 +1,72 @@ +dpt_airport->lon, $model->dpt_airport->lat], + [$model->arr_airport->lon, $model->arr_airport->lat], + ]); + + // TODO: Add markers for the start/end airports + // TODO: Read from the ACARS data table + + $feature = new Feature($line, [], 1); + $features = new FeatureCollection([$feature]); + + return [ + 'features' => $features, + //'center' => $center, + ]; + } + + /** + * 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; + } +} diff --git a/app/Services/PIREPService.php b/app/Services/PIREPService.php index 5b85481f..8fc36e95 100644 --- a/app/Services/PIREPService.php +++ b/app/Services/PIREPService.php @@ -193,7 +193,7 @@ class PIREPService extends BaseService /** * @param Pirep $pirep */ - public function setPilotState(Pirep &$pirep) + public function setPilotState(Pirep $pirep) { $pilot = $pirep->pilot; $pilot->refresh(); diff --git a/composer.lock b/composer.lock index a36ed31e..8dc2b0b6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "ec5e5013aa142d4d397b6d7fe00f6397", + "content-hash": "00894dc47f33576663d8967f821685e3", "packages": [ { "name": "composer/semver", @@ -427,7 +427,7 @@ "Doctrine\\DBAL\\": "lib/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -750,7 +750,7 @@ "Egulias\\EmailValidator\\": "EmailValidator" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -796,7 +796,7 @@ "Parsedown": "" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -856,7 +856,7 @@ "GuzzleHttp\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -914,7 +914,7 @@ "src/functions_include.php" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -969,7 +969,7 @@ "src/functions_include.php" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -1031,7 +1031,7 @@ "Hashids\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -1138,7 +1138,7 @@ "InfyOm\\AdminLTETemplates\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -1157,7 +1157,7 @@ "laravel", "templates" ], - "time": "2017-11-25T04:43:54+00:00" + "time": "2017-11-25 04:43:54" }, { "name": "infyomlabs/laravel-generator", @@ -1199,7 +1199,7 @@ "src/helpers.php" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -1223,7 +1223,7 @@ "test", "view" ], - "time": "2017-11-25T05:18:22+00:00" + "time": "2017-11-25 05:18:22" }, { "name": "jackiedo/timezonelist", @@ -1249,7 +1249,7 @@ "Jackiedo\\Timezonelist\\": "src/Jackiedo/Timezonelist" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -1326,6 +1326,59 @@ ], "time": "2016-12-07T09:37:55+00:00" }, + { + "name": "jmikola/geojson", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/jmikola/geojson.git", + "reference": "6ec3016cc0215667b7775f6ead7bd0337ad66eee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmikola/geojson/zipball/6ec3016cc0215667b7775f6ead7bd0337ad66eee", + "reference": "6ec3016cc0215667b7775f6ead7bd0337ad66eee", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~3.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "GeoJson\\": "src/" + }, + "classmap": [ + "stubs/" + ] + }, + "notification-url": "http://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeremy Mikola", + "email": "jmikola@gmail.com" + } + ], + "description": "GeoJSON implementation for PHP", + "homepage": "https://github.com/jmikola/geojson", + "keywords": [ + "geo", + "geojson", + "geospatial" + ], + "time": "2015-09-27T15:35:21+00:00" + }, { "name": "kkszymanowski/traitor", "version": "0.2.4", @@ -1353,7 +1406,7 @@ "Traitor\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -1541,7 +1594,7 @@ "Illuminate\\": "src/Illuminate/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -1964,7 +2017,7 @@ } ], "description": "Bloom filter implementation", - "time": "2017-11-30T17:51:14+00:00" + "time": "2017-11-30 17:51:14" }, { "name": "monolog/monolog", @@ -2070,7 +2123,7 @@ "Cron\\": "src/Cron/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -2119,7 +2172,7 @@ "src/DeepCopy/deep_copy.php" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -2164,11 +2217,11 @@ "VaCentral\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], - "time": "2017-12-08T04:00:06+00:00" + "time": "2017-12-08 04:00:06" }, { "name": "nesbot/carbon", @@ -2258,7 +2311,7 @@ "PhpParser\\": "lib/PhpParser" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -2691,7 +2744,7 @@ ] } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -2844,7 +2897,7 @@ "Prophecy\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -2962,7 +3015,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -3103,7 +3156,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -3184,7 +3237,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -3305,7 +3358,7 @@ "PragmaRX\\Version\\Tests\\": "tests/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -3365,7 +3418,7 @@ "PragmaRX\\Yaml\\Tests\\": "tests/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -3428,7 +3481,7 @@ "Prettus\\Repository\\": "src/Prettus/Repository/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -3897,7 +3950,7 @@ "Laratrust\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -4100,7 +4153,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -4675,7 +4728,7 @@ "src/helpers.php" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -4858,7 +4911,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -4907,7 +4960,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -4967,7 +5020,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -5023,7 +5076,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -5086,7 +5139,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -5135,7 +5188,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -5189,7 +5242,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -5277,7 +5330,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -5326,7 +5379,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -5478,7 +5531,7 @@ "bootstrap.php" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -5535,7 +5588,7 @@ "bootstrap.php" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -5594,7 +5647,7 @@ "Resources/stubs" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -5646,7 +5699,7 @@ "Symfony\\Polyfill\\Util\\": "" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -5701,7 +5754,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -5758,7 +5811,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -5841,7 +5894,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -5925,7 +5978,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -5993,7 +6046,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -6058,7 +6111,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -6120,7 +6173,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -6251,7 +6304,7 @@ "Tivie\\OS\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "APACHE 2.0" ], @@ -6472,7 +6525,7 @@ "Webpatser\\Uuid": "src/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -6601,7 +6654,7 @@ "src/helper.php" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -6658,7 +6711,7 @@ "Zend\\Diactoros\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "BSD-2-Clause" ], @@ -6884,7 +6937,7 @@ "Whoops\\": "src/Whoops/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], @@ -7226,7 +7279,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "http://packagist.org/downloads/", "license": [ "MIT" ], diff --git a/public/assets/system/js/system.js b/public/assets/system/js/system.js index 3ef69b3d..28eb487b 100644 --- a/public/assets/system/js/system.js +++ b/public/assets/system/js/system.js @@ -2,6 +2,13 @@ const phpvms= (function() { const draw_base_map = (opts) => { + opts = _.defaults(opts, { + render_elem: 'map', + zoom: 12, + layers: [], + set_marker: false, + }); + /*var openaip_airspace_labels = new L.TileLayer.WMS( "http://{s}.tile.maps.openaip.net/geowebcache/service/wms", { maxZoom: 14, @@ -51,8 +58,7 @@ const phpvms= (function() { attrib.addTo(map); return map; - } - + }; return { @@ -61,9 +67,41 @@ const phpvms= (function() { * @param opts */ render_route_map: (opts) => { + opts = _.defaults(opts, { - coords: [], // [ {name, lat, lon}, {name, lat, lon} ]; + features: null, // [ {name, lat, lon}, {name, lat, lon} ]; + center: [], + render_elem: 'map', + overlay_elem: '', + zoom: 5, + layers: [], + set_marker: false, }); + + console.log(opts.features); + + let map = draw_base_map(opts); + + var geodesicLayer = L.geodesic([], { + weight: 7, + opacity: 0.5, + color: '#ff33ee', + steps: 50, + wrap: false, + }).addTo(map); + + geodesicLayer.geoJson(opts.features) + map.fitBounds(geodesicLayer.getBounds()); + + /*let route = L.geoJSON(opts.features, { + "color": "#ff7800", + "weight": 5, + "opacity": 0.65 + });*/ + + //map.setView(opts.center, opts.zoom); + //map.fitBounds(route.getBounds()); + //route.addTo(map) }, /** diff --git a/public/assets/vendor/leaflet/leaflet.geodesic.js b/public/assets/vendor/leaflet/leaflet.geodesic.js new file mode 100644 index 00000000..63aa176f --- /dev/null +++ b/public/assets/vendor/leaflet/leaflet.geodesic.js @@ -0,0 +1,547 @@ +"use strict"; + +// This file is part of Leaflet.Geodesic. +// Copyright (C) 2017 Henry Thasler +// based on code by Chris Veness Copyright (C) 2014 https://github.com/chrisveness/geodesy +// +// Leaflet.Geodesic is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Leaflet.Geodesic is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Leaflet.Geodesic. If not, see . + + +/** Extend Number object with method to convert numeric degrees to radians */ +if (typeof Number.prototype.toRadians === "undefined") { + Number.prototype.toRadians = function () { + return this * Math.PI / 180 + } +} + +/** Extend Number object with method to convert radians to numeric (signed) degrees */ +if (typeof Number.prototype.toDegrees === "undefined") { + Number.prototype.toDegrees = function () { + return this * 180 / Math.PI + } +} + +const INTERSECT_LNG = 179.999 // Lng used for intersection and wrap around on map edges + +L.Geodesic = L.Polyline.extend({ + options: { + color: "blue", + steps: 10, + dash: 1, + wrap: true + }, + + initialize: function (latlngs, options) { + this.options = this._merge_options(this.options, options) + this.datum = {} + this.datum.ellipsoid = { + a: 6378137, + b: 6356752.3142, + f: 1 / 298.257223563 + } // WGS-84 + this._latlngs = (this.options.dash < 1) ? this._generate_GeodesicDashed( + latlngs) : this._generate_Geodesic(latlngs) + L.Polyline.prototype.initialize.call(this, this._latlngs, this.options) + }, + + setLatLngs: function (latlngs) { + this._latlngs = (this.options.dash < 1) ? this._generate_GeodesicDashed( + latlngs) : this._generate_Geodesic(latlngs) + L.Polyline.prototype.setLatLngs.call(this, this._latlngs) + }, + + /** + * Calculates some statistic values of current geodesic multipolyline + * @returns (Object} Object with several properties (e.g. overall distance) + */ + getStats: function () { + let obj = { + distance: 0, + points: 0, + polygons: this._latlngs.length + }, + poly, points + + for (poly = 0; poly < this._latlngs.length; poly++) { + obj.points += this._latlngs[poly].length + for (points = 0; points < (this._latlngs[poly].length - 1); points++) { + obj.distance += this._vincenty_inverse(this._latlngs[poly][points], + this._latlngs[poly][points + 1]).distance + } + } + return obj + }, + + + /** + * Creates geodesic lines from geoJson. Replaces all current features of this instance. + * Supports LineString, MultiLineString and Polygon + * @param {Object} geojson - geosjon as object. + */ + geoJson: function (geojson) { + + let normalized = L.GeoJSON.asFeature(geojson) + let features = normalized.type === "FeatureCollection" ? normalized.features : [ + normalized + ] + this._latlngs = [] + for (let feature of features) { + let geometry = feature.type === "Feature" ? feature.geometry : + feature, + coords = geometry.coordinates + + switch (geometry.type) { + case "LineString": + this._latlngs.push(this._generate_Geodesic([L.GeoJSON.coordsToLatLngs( + coords, 0)])) + break + case "MultiLineString": + case "Polygon": + this._latlngs.push(this._generate_Geodesic(L.GeoJSON.coordsToLatLngs( + coords, 1))) + break + case "Point": + case "MultiPoint": + console.log("Dude, points can't be drawn as geodesic lines...") + break + default: + console.log("Drawing " + geometry.type + + " as a geodesic is not supported. Skipping...") + } + } + L.Polyline.prototype.setLatLngs.call(this, this._latlngs) + }, + + /** + * Creates a great circle. Replaces all current lines. + * @param {Object} center - geographic position + * @param {number} radius - radius of the circle in metres + */ + createCircle: function (center, radius) { + let polylineIndex = 0 + let prev = { + lat: 0, + lng: 0, + brg: 0 + } + let step + + this._latlngs = [] + this._latlngs[polylineIndex] = [] + + let direct = this._vincenty_direct(L.latLng(center), 0, radius, this.options + .wrap) + prev = L.latLng(direct.lat, direct.lng) + this._latlngs[polylineIndex].push(prev) + for (step = 1; step <= this.options.steps;) { + direct = this._vincenty_direct(L.latLng(center), 360 / this.options + .steps * step, radius, this.options.wrap) + let gp = L.latLng(direct.lat, direct.lng) + if (Math.abs(gp.lng - prev.lng) > 180) { + let inverse = this._vincenty_inverse(prev, gp) + let sec = this._intersection(prev, inverse.initialBearing, { + lat: -89, + lng: ((gp.lng - prev.lng) > 0) ? -INTERSECT_LNG : INTERSECT_LNG + }, 0) + if (sec) { + this._latlngs[polylineIndex].push(L.latLng(sec.lat, sec.lng)) + polylineIndex++ + this._latlngs[polylineIndex] = [] + prev = L.latLng(sec.lat, -sec.lng) + this._latlngs[polylineIndex].push(prev) + } else { + polylineIndex++ + this._latlngs[polylineIndex] = [] + this._latlngs[polylineIndex].push(gp) + prev = gp + step++ + } + } else { + this._latlngs[polylineIndex].push(gp) + prev = gp + step++ + } + } + + L.Polyline.prototype.setLatLngs.call(this, this._latlngs) + }, + + /** + * Creates a geodesic Polyline from given coordinates + * @param {Object} latlngs - One or more polylines as an array. See Leaflet doc about Polyline + * @returns (Object} An array of arrays of geographical points. + */ + _generate_Geodesic: function (latlngs) { + let _geo = [], + _geocnt = 0, + s, poly, points, pointA, pointB + + for (poly = 0; poly < latlngs.length; poly++) { + _geo[_geocnt] = [] + for (points = 0; points < (latlngs[poly].length - 1); points++) { + pointA = L.latLng(latlngs[poly][points]) + pointB = L.latLng(latlngs[poly][points + 1]) + if (pointA.equals(pointB)) { + continue; + } + let inverse = this._vincenty_inverse(pointA, pointB) + let prev = pointA + _geo[_geocnt].push(prev) + for (s = 1; s <= this.options.steps;) { + let direct = this._vincenty_direct(pointA, inverse.initialBearing, + inverse.distance / this.options.steps * s, this.options.wrap + ) + let gp = L.latLng(direct.lat, direct.lng) + if (Math.abs(gp.lng - prev.lng) > 180) { + let sec = this._intersection(pointA, inverse.initialBearing, { + lat: -89, + lng: ((gp.lng - prev.lng) > 0) ? -INTERSECT_LNG : INTERSECT_LNG + }, 0) + if (sec) { + _geo[_geocnt].push(L.latLng(sec.lat, sec.lng)) + _geocnt++ + _geo[_geocnt] = [] + prev = L.latLng(sec.lat, -sec.lng) + _geo[_geocnt].push(prev) + } else { + _geocnt++ + _geo[_geocnt] = [] + _geo[_geocnt].push(gp) + prev = gp + s++ + } + } else { + _geo[_geocnt].push(gp) + prev = gp + s++ + } + } + } + _geocnt++ + } + return _geo + }, + + + /** + * Creates a dashed geodesic Polyline from given coordinates - under work + * @param {Object} latlngs - One or more polylines as an array. See Leaflet doc about Polyline + * @returns (Object} An array of arrays of geographical points. + */ + _generate_GeodesicDashed: function (latlngs) { + let _geo = [], + _geocnt = 0, + s, poly, points + // _geo = latlngs; // bypass + + for (poly = 0; poly < latlngs.length; poly++) { + _geo[_geocnt] = [] + for (points = 0; points < (latlngs[poly].length - 1); points++) { + let inverse = this._vincenty_inverse(L.latLng(latlngs[poly][ + points + ]), L.latLng(latlngs[poly][points + 1])) + let prev = L.latLng(latlngs[poly][points]) + _geo[_geocnt].push(prev) + for (s = 1; s <= this.options.steps;) { + let direct = this._vincenty_direct(L.latLng(latlngs[poly][ + points + ]), inverse.initialBearing, inverse.distance / this.options + .steps * s - inverse.distance / this.options.steps * (1 - + this.options.dash), this.options.wrap) + let gp = L.latLng(direct.lat, direct.lng) + if (Math.abs(gp.lng - prev.lng) > 180) { + let sec = this._intersection(L.latLng(latlngs[poly][points]), + inverse.initialBearing, { + lat: -89, + lng: ((gp.lng - prev.lng) > 0) ? -INTERSECT_LNG : INTERSECT_LNG + }, 0) + if (sec) { + _geo[_geocnt].push(L.latLng(sec.lat, sec.lng)) + _geocnt++ + _geo[_geocnt] = [] + prev = L.latLng(sec.lat, -sec.lng) + _geo[_geocnt].push(prev) + } else { + _geocnt++ + _geo[_geocnt] = [] + _geo[_geocnt].push(gp) + prev = gp + s++ + } + } else { + _geo[_geocnt].push(gp) + _geocnt++ + let direct2 = this._vincenty_direct(L.latLng(latlngs[poly][ + points + ]), inverse.initialBearing, inverse.distance / this.options + .steps * s, this.options.wrap) + _geo[_geocnt] = [] + _geo[_geocnt].push(L.latLng(direct2.lat, direct2.lng)) + s++ + } + } + } + _geocnt++ + } + return _geo + }, + + + /** + * Vincenty direct calculation. + * based on the work of Chris Veness (https://github.com/chrisveness/geodesy) + * + * @private + * @param {number} initialBearing - Initial bearing in degrees from north. + * @param {number} distance - Distance along bearing in metres. + * @returns (Object} Object including point (destination point), finalBearing. + */ + + _vincenty_direct: function (p1, initialBearing, distance, wrap) { + var φ1 = p1.lat.toRadians(), + λ1 = p1.lng.toRadians(); + var α1 = initialBearing.toRadians(); + var s = distance; + + var a = this.datum.ellipsoid.a, + b = this.datum.ellipsoid.b, + f = this.datum.ellipsoid.f; + + var sinα1 = Math.sin(α1); + var cosα1 = Math.cos(α1); + + var tanU1 = (1 - f) * Math.tan(φ1), + cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)), + sinU1 = tanU1 * cosU1; + var σ1 = Math.atan2(tanU1, cosα1); + var sinα = cosU1 * sinα1; + var cosSqα = 1 - sinα * sinα; + var uSq = cosSqα * (a * a - b * b) / (b * b); + var A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * + uSq))); + var B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq))); + + var σ = s / (b * A), + σʹ, iterations = 0; + do { + var cos2σM = Math.cos(2 * σ1 + σ); + var sinσ = Math.sin(σ); + var cosσ = Math.cos(σ); + var Δσ = B * sinσ * (cos2σM + B / 4 * (cosσ * (-1 + 2 * cos2σM * + cos2σM) - + B / 6 * cos2σM * (-3 + 4 * sinσ * sinσ) * (-3 + 4 * cos2σM * + cos2σM))); + σʹ = σ; + σ = s / (b * A) + Δσ; + } while (Math.abs(σ - σʹ) > 1e-12 && ++iterations); + + var x = sinU1 * sinσ - cosU1 * cosσ * cosα1; + var φ2 = Math.atan2(sinU1 * cosσ + cosU1 * sinσ * cosα1, (1 - f) * + Math.sqrt(sinα * sinα + x * x)); + var λ = Math.atan2(sinσ * sinα1, cosU1 * cosσ - sinU1 * sinσ * cosα1); + var C = f / 16 * cosSqα * (4 + f * (4 - 3 * cosSqα)); + var L = λ - (1 - C) * f * sinα * + (σ + C * sinσ * (cos2σM + C * cosσ * (-1 + 2 * cos2σM * cos2σM))); + + if (wrap) + var λ2 = (λ1 + L + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalise to -180...+180 + else + var λ2 = (λ1 + L); // do not normalize + + var revAz = Math.atan2(sinα, -x); + + return { + lat: φ2.toDegrees(), + lng: λ2.toDegrees(), + finalBearing: revAz.toDegrees() + }; + }, + + /** + * Vincenty inverse calculation. + * based on the work of Chris Veness (https://github.com/chrisveness/geodesy) + * + * @private + * @param {LatLng} p1 - Latitude/longitude of start point. + * @param {LatLng} p2 - Latitude/longitude of destination point. + * @returns {Object} Object including distance, initialBearing, finalBearing. + * @throws {Error} If formula failed to converge. + */ + _vincenty_inverse: function (p1, p2) { + var φ1 = p1.lat.toRadians(), + λ1 = p1.lng.toRadians(); + var φ2 = p2.lat.toRadians(), + λ2 = p2.lng.toRadians(); + + var a = this.datum.ellipsoid.a, + b = this.datum.ellipsoid.b, + f = this.datum.ellipsoid.f; + + var L = λ2 - λ1; + var tanU1 = (1 - f) * Math.tan(φ1), + cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)), + sinU1 = tanU1 * cosU1; + var tanU2 = (1 - f) * Math.tan(φ2), + cosU2 = 1 / Math.sqrt((1 + tanU2 * tanU2)), + sinU2 = tanU2 * cosU2; + + var λ = L, + λʹ, iterations = 0; + do { + var sinλ = Math.sin(λ), + cosλ = Math.cos(λ); + var sinSqσ = (cosU2 * sinλ) * (cosU2 * sinλ) + (cosU1 * sinU2 - + sinU1 * cosU2 * cosλ) * (cosU1 * sinU2 - sinU1 * cosU2 * cosλ); + var sinσ = Math.sqrt(sinSqσ); + if (sinσ == 0) return 0; // co-incident points + var cosσ = sinU1 * sinU2 + cosU1 * cosU2 * cosλ; + var σ = Math.atan2(sinσ, cosσ); + var sinα = cosU1 * cosU2 * sinλ / sinσ; + var cosSqα = 1 - sinα * sinα; + var cos2σM = cosσ - 2 * sinU1 * sinU2 / cosSqα; + if (isNaN(cos2σM)) cos2σM = 0; // equatorial line: cosSqα=0 (§6) + var C = f / 16 * cosSqα * (4 + f * (4 - 3 * cosSqα)); + λʹ = λ; + λ = L + (1 - C) * f * sinα * (σ + C * sinσ * (cos2σM + C * cosσ * (- + 1 + 2 * cos2σM * cos2σM))); + } while (Math.abs(λ - λʹ) > 1e-12 && ++iterations < 100); + if (iterations >= 100) { + console.log("Formula failed to converge. Altering target position.") + return this._vincenty_inverse(p1, { + lat: p2.lat, + lng: p2.lng - 0.01 + }) + // throw new Error('Formula failed to converge'); + } + + var uSq = cosSqα * (a * a - b * b) / (b * b); + var A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * + uSq))); + var B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq))); + var Δσ = B * sinσ * (cos2σM + B / 4 * (cosσ * (-1 + 2 * cos2σM * + cos2σM) - + B / 6 * cos2σM * (-3 + 4 * sinσ * sinσ) * (-3 + 4 * cos2σM * + cos2σM))); + + var s = b * A * (σ - Δσ); + + var fwdAz = Math.atan2(cosU2 * sinλ, cosU1 * sinU2 - sinU1 * cosU2 * + cosλ); + var revAz = Math.atan2(cosU1 * sinλ, -sinU1 * cosU2 + cosU1 * sinU2 * + cosλ); + + s = Number(s.toFixed(3)); // round to 1mm precision + return { + distance: s, + initialBearing: fwdAz.toDegrees(), + finalBearing: revAz.toDegrees() + }; + }, + + + /** + * Returns the point of intersection of two paths defined by point and bearing. + * based on the work of Chris Veness (https://github.com/chrisveness/geodesy) + * + * @param {LatLon} p1 - First point. + * @param {number} brng1 - Initial bearing from first point. + * @param {LatLon} p2 - Second point. + * @param {number} brng2 - Initial bearing from second point. + * @returns {Object} containing lat/lng information of intersection. + * + * @example + * var p1 = LatLon(51.8853, 0.2545), brng1 = 108.55; + * var p2 = LatLon(49.0034, 2.5735), brng2 = 32.44; + * var pInt = LatLon.intersection(p1, brng1, p2, brng2); // pInt.toString(): 50.9078°N, 4.5084°E + */ + _intersection: function (p1, brng1, p2, brng2) { + // see http://williams.best.vwh.net/avform.htm#Intersection + + var φ1 = p1.lat.toRadians(), + λ1 = p1.lng.toRadians(); + var φ2 = p2.lat.toRadians(), + λ2 = p2.lng.toRadians(); + var θ13 = Number(brng1).toRadians(), + θ23 = Number(brng2).toRadians(); + var Δφ = φ2 - φ1, + Δλ = λ2 - λ1; + + var δ12 = 2 * Math.asin(Math.sqrt(Math.sin(Δφ / 2) * Math.sin(Δφ / 2) + + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / + 2))); + if (δ12 == 0) return null; + + // initial/final bearings between points + var θ1 = Math.acos((Math.sin(φ2) - Math.sin(φ1) * Math.cos(δ12)) / + (Math.sin(δ12) * Math.cos(φ1))); + if (isNaN(θ1)) θ1 = 0; // protect against rounding + var θ2 = Math.acos((Math.sin(φ1) - Math.sin(φ2) * Math.cos(δ12)) / + (Math.sin(δ12) * Math.cos(φ2))); + + if (Math.sin(λ2 - λ1) > 0) { + var θ12 = θ1; + var θ21 = 2 * Math.PI - θ2; + } else { + var θ12 = 2 * Math.PI - θ1; + var θ21 = θ2; + } + + var α1 = (θ13 - θ12 + Math.PI) % (2 * Math.PI) - Math.PI; // angle 2-1-3 + var α2 = (θ21 - θ23 + Math.PI) % (2 * Math.PI) - Math.PI; // angle 1-2-3 + + if (Math.sin(α1) == 0 && Math.sin(α2) == 0) return null; // infinite intersections + if (Math.sin(α1) * Math.sin(α2) < 0) return null; // ambiguous intersection + + //α1 = Math.abs(α1); + //α2 = Math.abs(α2); + // ... Ed Williams takes abs of α1/α2, but seems to break calculation? + + var α3 = Math.acos(-Math.cos(α1) * Math.cos(α2) + + Math.sin(α1) * Math.sin(α2) * Math.cos(δ12)); + var δ13 = Math.atan2(Math.sin(δ12) * Math.sin(α1) * Math.sin(α2), + Math.cos(α2) + Math.cos(α1) * Math.cos(α3)) + var φ3 = Math.asin(Math.sin(φ1) * Math.cos(δ13) + + Math.cos(φ1) * Math.sin(δ13) * Math.cos(θ13)); + var Δλ13 = Math.atan2(Math.sin(θ13) * Math.sin(δ13) * Math.cos(φ1), + Math.cos(δ13) - Math.sin(φ1) * Math.sin(φ3)); + var λ3 = λ1 + Δλ13; + λ3 = (λ3 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalise to -180..+180º + + return { + lat: φ3.toDegrees(), + lng: λ3.toDegrees() + }; + }, + + /** + * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1 + * @param obj1 + * @param obj2 + * @returns obj3 a new object based on obj1 and obj2 + */ + _merge_options: function (obj1, obj2) { + let obj3 = {}; + for (let attrname in obj1) { + obj3[attrname] = obj1[attrname]; + } + for (let attrname in obj2) { + obj3[attrname] = obj2[attrname]; + } + return obj3; + } +}); + +L.geodesic = function (latlngs, options) { + return new L.Geodesic(latlngs, options); +}; diff --git a/resources/views/layouts/default/app.blade.php b/resources/views/layouts/default/app.blade.php index 8e586328..c8eb9b3b 100644 --- a/resources/views/layouts/default/app.blade.php +++ b/resources/views/layouts/default/app.blade.php @@ -169,11 +169,12 @@ +{{-- THESE LIBRARIES ARE REQUIRED FOR THINGS TO WORK PROPERLY! --}} - - + + @endsection