@fleetbase/fleetops-engine 0.4.20 → 0.4.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/composer.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetbase/fleetops-api",
3
- "version": "0.4.20",
3
+ "version": "0.4.22",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "keywords": [
6
6
  "fleetbase-extension",
package/extension.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Fleet-Ops",
3
- "version": "0.4.20",
3
+ "version": "0.4.22",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "repository": "https://github.com/fleetbase/fleetops",
6
6
  "license": "MIT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fleetbase/fleetops-engine",
3
- "version": "0.4.20",
3
+ "version": "0.4.22",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "fleetbase": {
6
6
  "route": "fleet-ops"
@@ -22,6 +22,25 @@ return [
22
22
  'db' => env('DB_CONNECTION', 'mysql')
23
23
  ],
24
24
 
25
+ /*
26
+ |--------------------------------------------------------------------------
27
+ | OSRM
28
+ |--------------------------------------------------------------------------
29
+ */
30
+ 'osrm' => [
31
+ 'host' => env('OSRM_HOST', 'https://bundle.routing.fleetbase.io')
32
+ ],
33
+
34
+ /*
35
+ |--------------------------------------------------------------------------
36
+ | Distance Matrix Calculator
37
+ | Options: "calculate", "google", "osrm"
38
+ |--------------------------------------------------------------------------
39
+ */
40
+ 'distance_matrix' => [
41
+ 'provider' => env('DISTANCE_MATRIX_PROVIDER', 'calculate')
42
+ ],
43
+
25
44
  /*
26
45
  |--------------------------------------------------------------------------
27
46
  | Navigator App
@@ -7,6 +7,7 @@ use Fleetbase\FleetOps\Models\Order;
7
7
  use Fleetbase\FleetOps\Support\Utils;
8
8
  use Fleetbase\LaravelMysqlSpatial\Types\Point;
9
9
  use Illuminate\Console\Command;
10
+ use Illuminate\Database\Eloquent\Collection;
10
11
  use Illuminate\Support\Carbon;
11
12
 
12
13
  class DispatchAdhocOrders extends Command
@@ -28,52 +29,56 @@ class DispatchAdhocOrders extends Command
28
29
  /**
29
30
  * Execute the console command.
30
31
  *
31
- * This method is responsible for the main logic of the command.
32
- * It fetches dispatchable orders, notifies about the current time and number of orders,
33
- * and then dispatches each order if there are nearby drivers.
34
- *
35
- * @return int
32
+ * Processes and dispatches ad-hoc orders based on their scheduled dispatch time and driver availability.
33
+ * Orders without an assigned driver and that have surpassed the dispatch time threshold are considered for dispatch.
34
+ * The command supports sandbox and testing modes for different operational environments.
36
35
  */
37
- public function handle()
36
+ public function handle(): void
38
37
  {
39
- // Set UTC as default timezone
40
38
  date_default_timezone_set('UTC');
41
39
 
42
- // Get all dispatchable orders
40
+ $sandboxMode = Utils::castBoolean($this->option('sandbox'));
41
+ $this->info('Running in ' . ($sandboxMode ? 'sandbox' : 'production') . ' mode.');
42
+
43
43
  $orders = $this->getDispatchableOrders();
44
44
 
45
- // Notify Current Time and # of Orders in Alera
46
- $this->alert('Found (' . $orders->count() . ') Orders to be Dispatched -- Current Time: ' . Carbon::now()->toDateTimeString());
45
+ $this->alert($orders->count() . ' orders found for ad-hoc dispatch. Current Time: ' . Carbon::now()->toDateTimeString());
47
46
 
48
- // Iterate for each order
49
- $orders->each(
50
- function ($order) {
51
- $pickup = $order->getPickupLocation();
52
- $distance = $order->getAdhocPingDistance();
47
+ foreach ($orders as $order) {
48
+ $pickup = $order->getPickupLocation();
49
+ $distance = $order->getAdhocPingDistance();
53
50
 
54
- if (!Utils::isPoint($pickup)) {
55
- return;
56
- }
51
+ if (!Utils::isPoint($pickup)) {
52
+ $this->error('Invalid pickup location for order ' . $order->public_id);
53
+ continue;
54
+ }
57
55
 
58
- // Get nearby drivers for this order
59
- $drivers = $this->getNearbyDriversForOrder($order, $pickup, $distance);
56
+ $drivers = $this->getNearbyDriversForOrder($order, $pickup, $distance);
60
57
 
61
- // Inform
62
- $this->alert('Found (' . $drivers->count() . ') driver within ' . $distance . ' meters of the pickup location for order ' . $order->public_id);
58
+ $this->line('Checking order ' . $order->public_id . ' for nearby drivers within ' . $distance . ' meters.');
63
59
 
64
- // Dispatch if there is nearby drivers
65
- if ($drivers->count()) {
66
- $order->dispatch(true);
67
- $this->info('Order ' . $order->public_id . ' dispatched!');
60
+ if ($drivers->count()) {
61
+ $order->dispatch(true);
62
+ $this->info('Order ' . $order->public_id . ' dispatched successfully to ' . $drivers->count() . ' nearby drivers.');
63
+ foreach ($drivers as $driver) {
64
+ $this->info('Pinging driver ' . $driver->name . ' (' . $driver->public_id . ') ...');
68
65
  }
66
+ } else {
67
+ $this->warn('No available drivers found for order ' . $order->public_id);
69
68
  }
70
- );
69
+ }
71
70
  }
72
71
 
73
72
  /**
74
- * Fetches dispatchable orders based on certain criteria.
73
+ * Fetches ad-hoc dispatchable orders based on certain criteria.
74
+ *
75
+ * Retrieves orders that are marked as ad-hoc and have not been dispatched yet. The orders
76
+ * are filtered by their scheduled dispatch time, order status, and whether a driver has been
77
+ * assigned to them. The method also applies filters based on sandbox mode.
78
+ *
79
+ * @return Collection returns a collection of orders that meet the criteria
75
80
  */
76
- public function getDispatchableOrders(): \Illuminate\Database\Eloquent\Collection
81
+ public function getDispatchableOrders(): Collection
77
82
  {
78
83
  $sandbox = Utils::castBoolean($this->option('sandbox'));
79
84
  $interval = 4;
@@ -100,8 +105,17 @@ class DispatchAdhocOrders extends Command
100
105
 
101
106
  /**
102
107
  * Fetches nearby drivers for a given order based on the pickup location and distance.
108
+ *
109
+ * Retrieves drivers who are within a specified distance from the pickup location of the order.
110
+ * The method supports a testing mode to simulate driver availability.
111
+ *
112
+ * @param Order $order the order for which drivers are being sought
113
+ * @param Point $pickup the geographic point representing the pickup location
114
+ * @param int $distance the maximum distance (in meters) within which drivers should be located
115
+ *
116
+ * @return Collection returns a collection of nearby drivers
103
117
  */
104
- public function getNearbyDriversForOrder(Order $order, Point $pickup, int $distance): \Illuminate\Database\Eloquent\Collection
118
+ public function getNearbyDriversForOrder(Order $order, Point $pickup, int $distance): Collection
105
119
  {
106
120
  $testing = Utils::castBoolean($this->option('testing'));
107
121
 
@@ -14,7 +14,7 @@ class DispatchOrders extends Command
14
14
  *
15
15
  * @var string
16
16
  */
17
- protected $signature = 'fleetops:dispatch-orders {--sandbox=false}';
17
+ protected $signature = 'fleetops:dispatch-orders {--sandbox=false : Whether to use the sandbox database for testing purposes.}';
18
18
 
19
19
  /**
20
20
  * The console command description.
@@ -26,41 +26,47 @@ class DispatchOrders extends Command
26
26
  /**
27
27
  * Execute the console command.
28
28
  *
29
- * @return int
29
+ * Processes and dispatches orders that are scheduled for today. Orders are
30
+ * filtered based on their dispatch status, scheduled time, and other criteria.
31
+ * The command allows for a sandbox mode for testing.
30
32
  */
31
- public function handle()
33
+ public function handle(): void
32
34
  {
33
35
  // Set UTC as default timezone
34
36
  date_default_timezone_set('UTC');
35
37
 
38
+ $sandboxMode = Utils::castBoolean($this->option('sandbox'));
39
+ $this->info('Running in ' . ($sandboxMode ? 'sandbox' : 'production') . ' mode.');
40
+
36
41
  // Get all scheduled dispatchable orders
37
- $orders = $this->getScheduledOrders();
42
+ $orders = $this->getScheduledOrders($sandboxMode);
38
43
 
39
- // Notify Current Time and # of Orders in Alera
40
- $this->alert('Found (' . $orders->count() . ') Orders Scheduled for Dispatched -- Current Time: ' . Carbon::now()->toDateTimeString());
44
+ $this->alert('Found ' . $orders->count() . ' orders scheduled for dispatch. Current Time: ' . Carbon::now()->toDateTimeString());
41
45
 
42
46
  // Dispatch each order
43
- $orders->each(
44
- function ($order) {
45
- if ($order->shouldDispatch()) {
46
- $order->dispatch();
47
-
48
- $this->info('Order ' . $order->public_id . ' dispatched! (' . $order->scheduled_at . ')');
49
- } else {
50
- $this->warn('Order ' . $order->public_id . ' will be dispatched today today AT ' . $order->scheduled_at);
51
- }
47
+ foreach ($orders as $order) {
48
+ if ($order->shouldDispatch()) {
49
+ $order->dispatch();
50
+ $this->info('Order ' . $order->public_id . ' dispatched successfully (' . $order->scheduled_at . ').');
51
+ } else {
52
+ $this->warn('Order ' . $order->public_id . ' is not ready for dispatch (' . $order->scheduled_at . ').');
52
53
  }
53
- );
54
+ }
54
55
  }
55
56
 
56
57
  /**
57
- * Fetches scheduled dispatchable orders based on certain criteria.
58
+ * Fetches scheduled dispatchable orders.
59
+ *
60
+ * Retrieves orders that are scheduled for today and have not yet been dispatched.
61
+ * Filters orders based on dispatch status, order status, and scheduled date.
62
+ *
63
+ * @param bool $sandboxMode whether to use the sandbox database
64
+ *
65
+ * @return \Illuminate\Database\Eloquent\Collection returns a collection of orders that meet the criteria
58
66
  */
59
- public function getScheduledOrders(): \Illuminate\Database\Eloquent\Collection
67
+ protected function getScheduledOrders(bool $sandboxMode): \Illuminate\Database\Eloquent\Collection
60
68
  {
61
- $sandbox = Utils::castBoolean($this->option('sandbox'));
62
-
63
- return Order::on($sandbox ? 'sandbox' : 'mysql')
69
+ return Order::on($sandboxMode ? 'sandbox' : 'mysql')
64
70
  ->withoutGlobalScopes()
65
71
  ->where('dispatched', 0)
66
72
  ->whereIn('status', ['pending', 'created'])
@@ -0,0 +1,97 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Console\Commands;
4
+
5
+ use Illuminate\Console\Command;
6
+ use Illuminate\Support\Str;
7
+
8
+ class FixInvalidPolymorphicRelationTypeNamespaces extends Command
9
+ {
10
+ /**
11
+ * The name and signature of the console command.
12
+ *
13
+ * @var string
14
+ */
15
+ protected $signature = 'fleetops:fix-invalid-polymorphic-relation-type-namespaces';
16
+
17
+ /**
18
+ * The console command description.
19
+ *
20
+ * @var string
21
+ */
22
+ protected $description = 'Fixes invalid namespaces in polymorphic relations across multiple models';
23
+
24
+ /**
25
+ * Handle the console command execution.
26
+ *
27
+ * Iterates over a predefined list of models and their respective polymorphic columns,
28
+ * calling a method to fix invalid namespace references in these polymorphic relations.
29
+ * This allows correction across multiple models in a single command execution.
30
+ *
31
+ * @return void
32
+ */
33
+ public function handle()
34
+ {
35
+ $models = [
36
+ ['model' => \Fleetbase\FleetOps\Models\Order::class, 'columns' => ['customer_type', 'facilitator_type']],
37
+ ['model' => \Fleetbase\FleetOps\Models\Place::class, 'columns' => ['owner_type']],
38
+ ['model' => \Fleetbase\FleetOps\Models\Entity::class, 'columns' => ['customer_type']],
39
+ ['model' => \Fleetbase\FleetOps\Models\PurchaseRate::class, 'columns' => ['customer_type']],
40
+ ];
41
+
42
+ foreach ($models as $modelConfig) {
43
+ $this->fixModelRelations($modelConfig['model'], $modelConfig['columns']);
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Fixes invalid namespace references in polymorphic relations for a specific model.
49
+ *
50
+ * Given a model class and an array of its polymorphic relation columns, this method
51
+ * iterates through each record of the model, correcting any invalid namespaces in
52
+ * these columns. It outputs information about the process, including the number of
53
+ * records updated. This function is designed to be reusable for different models.
54
+ *
55
+ * @param string $modelClass the class name of the model to be processed
56
+ * @param array $columns an array of column names that have polymorphic relations
57
+ */
58
+ protected function fixModelRelations(string $modelClass, array $columns): void
59
+ {
60
+ $modelInstance = new $modelClass();
61
+ $modelName = class_basename($modelInstance);
62
+
63
+ $this->info("Processing $modelName...");
64
+
65
+ $records = $modelInstance::query();
66
+ foreach ($columns as $column) {
67
+ $records->orWhereNotNull($column);
68
+ }
69
+ $records = $records->get();
70
+ $updatedCount = 0;
71
+ $this->alert('Checking ' . $records->count() . ' ' . Str::plural($modelName, $records->count()) . ' for invalid polymorphic relation type namespaces.');
72
+
73
+ foreach ($records as $record) {
74
+ foreach ($columns as $column) {
75
+ if (isset($record->$column)) {
76
+ $original = $record->$column;
77
+ $normalizedOriginal = ltrim($original, '\\');
78
+
79
+ if (Str::startsWith($normalizedOriginal, 'Fleetbase\Models')) {
80
+ $newNamespace = Str::replaceFirst('Fleetbase\Models', 'Fleetbase\FleetOps\Models', $normalizedOriginal);
81
+ $record->$column = $newNamespace;
82
+ $updatedCount++;
83
+
84
+ $this->info("{$modelName} ID {$record->id}: Corrected namespace from '$original' to '$newNamespace'.");
85
+ }
86
+ }
87
+ }
88
+
89
+ if ($record->isDirty()) {
90
+ $record->save();
91
+ $this->info("Saved changes for $modelName ID {$record->public_id}.");
92
+ }
93
+ }
94
+
95
+ $this->info("Finished processing $modelName. Total updated records: $updatedCount.");
96
+ }
97
+ }
@@ -13,7 +13,7 @@ class TrackOrderDistanceAndTime extends Command
13
13
  *
14
14
  * @var string
15
15
  */
16
- protected $signature = 'fleetops:update-estimations';
16
+ protected $signature = 'fleetops:update-estimations {--provider : The distance and time calculation provider (calculate, google, or osrm)}';
17
17
 
18
18
  /**
19
19
  * The console command description.
@@ -32,31 +32,64 @@ class TrackOrderDistanceAndTime extends Command
32
32
  // Set UTC as default timezone
33
33
  date_default_timezone_set('UTC');
34
34
 
35
- // Get all active/ready order
36
- $orders = $this->getActiveOrders();
35
+ // Determine the provider
36
+ $provider = $this->option('provider');
37
+ if (!$provider) {
38
+ $provider = config('fleetops.distance_matrix.provider');
39
+ }
40
+ $this->info("Using '{$provider}' as the provider for calculations.");
37
41
 
38
- // Track updated orders
39
- $updated = [];
42
+ // Get all active/ready orders
43
+ $orders = $this->getActiveOrders();
40
44
 
41
- // Notify Current Time and # of Orders in Alera
42
- $this->alert('Found (' . $orders->count() . ') Orders to update Tracking Estimation -- Current Time: ' . Carbon::now()->toDateTimeString());
45
+ // Notify Current Time and # of Orders
46
+ $this->alert('Found ' . $orders->count() . ' orders to update tracking estimations. Current Time: ' . Carbon::now()->toDateTimeString());
43
47
 
44
48
  // Update for each order
45
- $orders->each(
46
- function ($order) use (&$updated) {
47
- $updated[] = $order->setDistanceAndTime()->id;
49
+ $updated = 0;
50
+ foreach ($orders as $order) {
51
+ try {
52
+ // Update order distance and time estimations based on the provider
53
+ $order->setDistanceAndTime(['provider' => $provider]);
54
+ $updated++;
55
+ $this->info("Order ID {$order->id} - Distance & Time estimations updated.");
56
+ } catch (\Exception $e) {
57
+ $this->error("Failed to update Order ID {$order->id} - Error: " . $e->getMessage());
48
58
  }
49
- );
59
+ }
50
60
 
51
- // Update info
52
- $this->info('Updated ' . count($updated) . '/' . $orders->count() . ' orders Distance & Time Estimations.');
61
+ // Summary info
62
+ $this->info("Updated $updated/" . $orders->count() . " orders' Distance & Time Estimations.");
53
63
  }
54
64
 
55
65
  /**
56
- * Fetches active orders.
66
+ * Retrieves active orders that meet specific criteria.
67
+ *
68
+ * This method returns a collection of active orders with the following conditions:
69
+ * - Not in 'completed' or 'canceled' status.
70
+ * - Not marked as deleted (`deleted_at` is null).
71
+ * - Associated with a company (`company_uuid` is not null).
72
+ * - The order process has started (`started` is not null).
73
+ * - Contains a payload (`payload` relationship exists).
74
+ * - Created within the past month.
75
+ *
76
+ * The result includes related payload data, waypoints, and information about pickup and dropoff points.
77
+ * Global scopes are not applied to this query.
78
+ *
79
+ * @return \Illuminate\Database\Eloquent\Collection collection of relevant Order objects
57
80
  */
58
- public function getActiveOrders(): \Illuminate\Database\Eloquent\Collection
81
+ protected function getActiveOrders()
59
82
  {
60
- return Order::whereNotIn('status', ['completed', 'canceled'])->whereNull(['deleted_at'])->whereNotNull('company_uuid')->whereHas('payload')->with(['payload', 'payload.waypoints', 'payload.pickup', 'payload.dropoff'])->withoutGlobalScopes()->get();
83
+ $oneMonthAgo = Carbon::now()->subMonth();
84
+
85
+ return Order::whereNotIn('status', ['completed', 'canceled'])
86
+ ->whereNull('deleted_at')
87
+ ->whereNotNull('company_uuid')
88
+ ->whereNotNull('started')
89
+ ->where('created_at', '>=', $oneMonthAgo)
90
+ ->whereHas('payload')
91
+ ->with(['payload', 'payload.waypoints', 'payload.pickup', 'payload.dropoff'])
92
+ ->withoutGlobalScopes()
93
+ ->get();
61
94
  }
62
95
  }
@@ -1505,13 +1505,11 @@ class Order extends Model
1505
1505
  *
1506
1506
  * @return Order the updated Order instance
1507
1507
  */
1508
- public function setDistanceAndTime(): Order
1508
+ public function setDistanceAndTime($options = []): Order
1509
1509
  {
1510
1510
  $origin = $this->getCurrentOriginPosition();
1511
1511
  $destination = $this->getDestinationPosition();
1512
-
1513
- $matrix = Utils::getDrivingDistanceAndTime($origin, $destination);
1514
-
1512
+ $matrix = Utils::getDrivingDistanceAndTime($origin, $destination, $options);
1515
1513
  if ($origin === null || $destination === null) {
1516
1514
  return $this;
1517
1515
  }
@@ -3,6 +3,7 @@
3
3
  namespace Fleetbase\FleetOps\Models;
4
4
 
5
5
  use Fleetbase\Casts\Json;
6
+ use Fleetbase\Casts\PolymorphicType;
6
7
  use Fleetbase\FleetOps\Casts\Point;
7
8
  use Fleetbase\FleetOps\Support\Geocoding;
8
9
  use Fleetbase\FleetOps\Support\Utils;
@@ -114,8 +115,9 @@ class Place extends Model
114
115
  * @var array
115
116
  */
116
117
  protected $casts = [
117
- 'meta' => Json::class,
118
- 'location' => Point::class,
118
+ 'meta' => Json::class,
119
+ 'location' => Point::class,
120
+ 'owner_type' => PolymorphicType::class,
119
121
  ];
120
122
 
121
123
  /**
@@ -3,6 +3,7 @@
3
3
  namespace Fleetbase\FleetOps\Models;
4
4
 
5
5
  use Fleetbase\Casts\Json;
6
+ use Fleetbase\Casts\PolymorphicType;
6
7
  use Fleetbase\FleetOps\Support\Utils;
7
8
  use Fleetbase\Models\Model;
8
9
  use Fleetbase\Traits\HasApiModelBehavior;
@@ -57,7 +58,8 @@ class PurchaseRate extends Model
57
58
  * @var array
58
59
  */
59
60
  protected $casts = [
60
- 'meta' => Json::class,
61
+ 'meta' => Json::class,
62
+ 'customer_type' => PolymorphicType::class,
61
63
  ];
62
64
 
63
65
  /**
@@ -48,6 +48,7 @@ class FleetOpsServiceProvider extends CoreServiceProvider
48
48
  \Fleetbase\FleetOps\Console\Commands\TrackOrderDistanceAndTime::class,
49
49
  \Fleetbase\FleetOps\Console\Commands\FixDriverCompanies::class,
50
50
  \Fleetbase\FleetOps\Console\Commands\FixLegacyOrderConfigs::class,
51
+ \Fleetbase\FleetOps\Console\Commands\FixInvalidPolymorphicRelationTypeNamespaces::class,
51
52
  ];
52
53
 
53
54
  /**
@@ -28,28 +28,4 @@ class Algo
28
28
 
29
29
  return $result;
30
30
  }
31
-
32
- /**
33
- * Calculates driving distance and time using Google distance matric.
34
- *
35
- * @return array
36
- */
37
- public static function calculateDrivingDistanceAndTime($lat1, $long1, $lat2, $long2)
38
- {
39
- $url =
40
- 'https://maps.googleapis.com/maps/api/distancematrix/json?origins=' . $lat1 . ',' . $long1 . '&destinations=' . $lat2 . ',' . $long2 . '&mode=driving&key=' . config('services.google.key');
41
- $ch = curl_init();
42
- curl_setopt($ch, CURLOPT_URL, $url);
43
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
44
- curl_setopt($ch, CURLOPT_PROXYPORT, 3128);
45
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
46
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
47
- $response = curl_exec($ch);
48
- curl_close($ch);
49
- $response_a = json_decode($response, true);
50
- $dist = $response_a['rows'][0]['elements'][0]['distance']['value'];
51
- $time = $response_a['rows'][0]['elements'][0]['duration']['value'];
52
-
53
- return ['distance' => $dist, 'time' => $time];
54
- }
55
31
  }
@@ -14,7 +14,7 @@ namespace Fleetbase\FleetOps\Support;
14
14
  * @license https://opensource.org/licenses/MIT MIT License
15
15
  * @author Ronald A. Richardson
16
16
  */
17
- class DistanceMatrix
17
+ class DistanceMatrix implements \JsonSerializable
18
18
  {
19
19
  public ?float $distance;
20
20
  public ?float $time;
@@ -24,4 +24,12 @@ class DistanceMatrix
24
24
  $this->distance = $distance;
25
25
  $this->time = $time;
26
26
  }
27
+
28
+ public function jsonSerialize()
29
+ {
30
+ return [
31
+ 'distance' => $this->distance,
32
+ 'time' => $this->time,
33
+ ];
34
+ }
27
35
  }
@@ -35,6 +35,13 @@ class OSRM
35
35
  }
36
36
 
37
37
  $coordinates = "{$start->getLng()},{$start->getLat()};{$end->getLng()},{$end->getLat()}";
38
+
39
+ return static::getRouteFromCoordinatesString($coordinates, $queryParameters);
40
+ }
41
+
42
+ public static function getRouteFromCoordinatesString(string $coordinates, array $queryParameters = [])
43
+ {
44
+ $cacheKey = 'getRouteFromCoordinatesString:' . md5($coordinates . serialize($queryParameters));
38
45
  $url = self::$baseUrl . "/route/v1/driving/{$coordinates}";
39
46
  $response = Http::get($url, $queryParameters);
40
47
  $data = $response->json();
@@ -3,6 +3,7 @@
3
3
  namespace Fleetbase\FleetOps\Support;
4
4
 
5
5
  use Fleetbase\FleetOps\Flow\Activity;
6
+ use Fleetbase\LaravelMysqlSpatial\Types\Point;
6
7
  use Fleetbase\Support\Utils as FleetbaseUtils;
7
8
  use Illuminate\Support\Arr;
8
9
  use Illuminate\Support\Facades\DB;
@@ -95,11 +96,11 @@ class Utils extends FleetbaseUtils
95
96
  /**
96
97
  * Unpacks a mysql POINT column from binary to array.
97
98
  */
98
- public static function mysqlPointAsGeometry(string $rawPoint): \Fleetbase\LaravelMysqlSpatial\Types\Point
99
+ public static function mysqlPointAsGeometry(string $rawPoint): Point
99
100
  {
100
101
  $coordinates = static::unpackPoint($rawPoint);
101
102
 
102
- return new \Fleetbase\LaravelMysqlSpatial\Types\Point($coordinates['lon'], $coordinates['lat']);
103
+ return new Point($coordinates['lon'], $coordinates['lat']);
103
104
  }
104
105
 
105
106
  /**
@@ -162,7 +163,7 @@ class Utils extends FleetbaseUtils
162
163
  public static function isCoordinates($coordinates): bool
163
164
  {
164
165
  try {
165
- return static::getPointFromMixed($coordinates) instanceof \Fleetbase\LaravelMysqlSpatial\Types\Point;
166
+ return static::getPointFromMixed($coordinates) instanceof Point;
166
167
  } catch (\Throwable $e) {
167
168
  return false;
168
169
  }
@@ -175,9 +176,9 @@ class Utils extends FleetbaseUtils
175
176
  *
176
177
  * @param mixed $coordinates the coordinates input value to extract a coordinate from
177
178
  *
178
- * @return \Fleetbase\LaravelMysqlSpatial\Types\Point the extracted Point object
179
+ * @return Point the extracted Point object
179
180
  */
180
- public static function getPointFromMixed($coordinates): ?\Fleetbase\LaravelMysqlSpatial\Types\Point
181
+ public static function getPointFromMixed($coordinates): ?Point
181
182
  {
182
183
  $latitude = null;
183
184
  $longitude = null;
@@ -199,7 +200,7 @@ class Utils extends FleetbaseUtils
199
200
  $coordinates = $coordinates->getSpatialValue();
200
201
  }
201
202
 
202
- if ($coordinates instanceof \Fleetbase\LaravelMysqlSpatial\Types\Point) {
203
+ if ($coordinates instanceof Point) {
203
204
  $latitude = $coordinates->getLat();
204
205
  $longitude = $coordinates->getLng();
205
206
  } elseif (static::isGeoJson($coordinates)) {
@@ -216,7 +217,7 @@ class Utils extends FleetbaseUtils
216
217
 
217
218
  if (static::isJson($coordinatesJson)) {
218
219
  try {
219
- $coordinates = \Fleetbase\LaravelMysqlSpatial\Types\Point::fromJson($coordinatesJson);
220
+ $coordinates = Point::fromJson($coordinatesJson);
220
221
  } catch (\Throwable $e) {
221
222
  if ($coordinatesInGeoJson) {
222
223
  return static::getPointFromMixed($coordinatesInGeoJson);
@@ -312,7 +313,7 @@ class Utils extends FleetbaseUtils
312
313
  $longitude = $coords[1];
313
314
  }
314
315
 
315
- if ($coordinates instanceof \Fleetbase\LaravelMysqlSpatial\Types\Point) {
316
+ if ($coordinates instanceof Point) {
316
317
  return $coordinates;
317
318
  }
318
319
 
@@ -321,7 +322,7 @@ class Utils extends FleetbaseUtils
321
322
  throw new \Exception('Attempted to resolve Point from invalid location.');
322
323
  }
323
324
 
324
- return new \Fleetbase\LaravelMysqlSpatial\Types\Point((float) $latitude, (float) $longitude);
325
+ return new Point((float) $latitude, (float) $longitude);
325
326
  }
326
327
 
327
328
  /**
@@ -334,7 +335,7 @@ class Utils extends FleetbaseUtils
334
335
  */
335
336
  public static function isCoordinatesStrict($coordinates)
336
337
  {
337
- return static::getPointFromCoordinatesStrict($coordinates) instanceof \Fleetbase\LaravelMysqlSpatial\Types\Point;
338
+ return static::getPointFromCoordinatesStrict($coordinates) instanceof Point;
338
339
  }
339
340
 
340
341
  /**
@@ -343,15 +344,15 @@ class Utils extends FleetbaseUtils
343
344
  *
344
345
  * @param mixed $coordinates the coordinates, which can be an array, object, or string
345
346
  *
346
- * @return \Fleetbase\LaravelMysqlSpatial\Types\Point Returns the Point instance
347
+ * @return Point Returns the Point instance
347
348
  */
348
- public static function getPointFromCoordinatesStrict($coordinates): ?\Fleetbase\LaravelMysqlSpatial\Types\Point
349
+ public static function getPointFromCoordinatesStrict($coordinates): ?Point
349
350
  {
350
351
  if ($coordinates instanceof \Fleetbase\LaravelMysqlSpatial\Eloquent\SpatialExpression) {
351
352
  $coordinates = $coordinates->getSpatialValue();
352
353
  }
353
354
 
354
- if ($coordinates instanceof \Fleetbase\LaravelMysqlSpatial\Types\Point) {
355
+ if ($coordinates instanceof Point) {
355
356
  return $coordinates;
356
357
  }
357
358
 
@@ -363,7 +364,7 @@ class Utils extends FleetbaseUtils
363
364
  }
364
365
 
365
366
  if (static::isJson($coordinatesJson)) {
366
- $coordinates = \Fleetbase\LaravelMysqlSpatial\Types\Point::fromJson($coordinatesJson);
367
+ $coordinates = Point::fromJson($coordinatesJson);
367
368
  }
368
369
  }
369
370
 
@@ -372,7 +373,7 @@ class Utils extends FleetbaseUtils
372
373
  $longitude = static::or($coordinates, ['lon', '_lon', 'long', 'lng', '_lng', '_longitude', 'longitude', 'y', '1']);
373
374
 
374
375
  if (is_numeric($latitude) && is_numeric($longitude)) {
375
- $coordinates = new \Fleetbase\LaravelMysqlSpatial\Types\Point((float) $latitude, (float) $longitude);
376
+ $coordinates = new Point((float) $latitude, (float) $longitude);
376
377
  }
377
378
  }
378
379
 
@@ -412,11 +413,11 @@ class Utils extends FleetbaseUtils
412
413
  $longitude = $coords[1];
413
414
 
414
415
  if (is_numeric($latitude) && is_numeric($longitude)) {
415
- $coordinates = new \Fleetbase\LaravelMysqlSpatial\Types\Point((float) $latitude, (float) $longitude);
416
+ $coordinates = new Point((float) $latitude, (float) $longitude);
416
417
  }
417
418
  }
418
419
 
419
- if ($coordinates instanceof \Fleetbase\LaravelMysqlSpatial\Types\Point) {
420
+ if ($coordinates instanceof Point) {
420
421
  return $coordinates;
421
422
  }
422
423
 
@@ -444,7 +445,7 @@ class Utils extends FleetbaseUtils
444
445
  $coordinates = $coordinates->location;
445
446
  }
446
447
 
447
- if ($coordinates instanceof \Fleetbase\LaravelMysqlSpatial\Types\Point) {
448
+ if ($coordinates instanceof Point) {
448
449
  $latitude = $coordinates->getLat();
449
450
  $longitude = $coordinates->getLng();
450
451
  } elseif (is_array($coordinates) || is_object($coordinates)) {
@@ -534,22 +535,22 @@ class Utils extends FleetbaseUtils
534
535
  *
535
536
  * @param mixed $coordinates the input value representing a location or set of coordinates
536
537
  *
537
- * @return \Fleetbase\LaravelMysqlSpatial\Types\Point the extracted point value
538
+ * @return Point the extracted point value
538
539
  */
539
- public static function getPointFromCoordinates($coordinates): \Fleetbase\LaravelMysqlSpatial\Types\Point
540
+ public static function getPointFromCoordinates($coordinates): Point
540
541
  {
541
- if ($coordinates instanceof \Fleetbase\LaravelMysqlSpatial\Types\Point) {
542
+ if ($coordinates instanceof Point) {
542
543
  return $coordinates;
543
544
  }
544
545
 
545
546
  if (is_null($coordinates) || !static::isCoordinates($coordinates)) {
546
- return new \Fleetbase\LaravelMysqlSpatial\Types\Point(0, 0);
547
+ return new Point(0, 0);
547
548
  }
548
549
 
549
550
  $latitude = static::getLatitudeFromCoordinates($coordinates);
550
551
  $longitude = static::getLongitudeFromCoordinates($coordinates);
551
552
 
552
- return new \Fleetbase\LaravelMysqlSpatial\Types\Point($latitude, $longitude);
553
+ return new Point($latitude, $longitude);
553
554
  }
554
555
 
555
556
  /**
@@ -563,19 +564,19 @@ class Utils extends FleetbaseUtils
563
564
  {
564
565
  $wkt = 'POINT(0 0)';
565
566
 
566
- if ($point instanceof \Fleetbase\LaravelMysqlSpatial\Types\Point) {
567
+ if ($point instanceof Point) {
567
568
  $wkt = $point->toWKT();
568
569
  }
569
570
 
570
571
  if (is_array($point)) {
571
572
  $json = json_encode($point);
572
- $p = \Fleetbase\LaravelMysqlSpatial\Types\Point::fromJson($json);
573
+ $p = Point::fromJson($json);
573
574
 
574
575
  $wkt = $p->toWkt();
575
576
  }
576
577
 
577
578
  if (is_string($point)) {
578
- $p = \Fleetbase\LaravelMysqlSpatial\Types\Point::fromString($point);
579
+ $p = Point::fromString($point);
579
580
 
580
581
  $wkt = $p->toWKT();
581
582
  }
@@ -605,133 +606,75 @@ class Utils extends FleetbaseUtils
605
606
  *
606
607
  * @param string $data the binary string representing the point
607
608
  *
608
- * @return \Fleetbase\LaravelMysqlSpatial\Types\Point the Laravel MySQL Spatial Point instance
609
+ * @return Point the Laravel MySQL Spatial Point instance
609
610
  */
610
- public static function rawPointToPoint(string $data): \Fleetbase\LaravelMysqlSpatial\Types\Point
611
+ public static function rawPointToPoint(string $data): Point
611
612
  {
612
613
  // Use the `unpack` function to extract the X, Y, and SRID values from the binary string.
613
614
  $res = unpack('lSRID/CByteOrder/lTypeInfo/dX/dY', $data);
614
615
 
615
616
  // Return a new Laravel MySQL Spatial Point instance with the X, Y, and SRID values.
616
- return new \Fleetbase\LaravelMysqlSpatial\Types\Point($res['X'], $res['Y'], $res['SRID']);
617
+ return new Point($res['X'], $res['Y'], $res['SRID']);
617
618
  }
618
619
 
619
620
  /**
620
- * Calculates driving distance and time using Google distance matrix.
621
- * Returns distance in meters and time in seconds.
621
+ * Calculates the driving distance and time between two points.
622
622
  *
623
- * @param \Fleetbase\FleetOps\Models\Place|\Fleetbase\LaravelMysqlSpatial\Types\Point|array $origin
624
- * @param \Fleetbase\FleetOps\Models\Place|\Fleetbase\LaravelMysqlSpatial\Types\Point|array $destination
623
+ * This function attempts to find the driving distance and time from the origin to the destination
624
+ * using various map APIs. It first attempts to resolve the origin and destination from various input types
625
+ * into geographical coordinates. Then, based on the provider specified in the options or defaulting to configured provider,
626
+ * it queries the respective service for the driving distance and time. The result is returned as a DistanceMatrix
627
+ * object with the distance in meters and the time in seconds.
628
+ *
629
+ * @param mixed $origin The origin of the journey. Can be a Place model, a string, or an array with latitude and longitude.
630
+ * @param mixed $destination The destination of the journey. Similar to the origin, it accepts multiple input types.
631
+ *
632
+ * @return DistanceMatrix an object containing the distance and time of the journey
633
+ *
634
+ * @throws \Throwable throws any exception encountered during the resolution of points or API calls
625
635
  */
626
- public static function getDrivingDistanceAndTime($origin, $destination): DistanceMatrix
636
+ public static function getDrivingDistanceAndTime($origin, $destination, $options = []): DistanceMatrix
627
637
  {
628
- if ($origin instanceof \Fleetbase\FleetOps\Models\Place) {
629
- $origin = static::createObject(
630
- [
631
- 'latitude' => $origin->location->getLat(),
632
- 'longitude' => $origin->location->getLng(),
633
- ]
634
- );
635
- } else {
636
- try {
637
- $point = static::getPointFromMixed($origin);
638
- $origin = static::createObject(
639
- [
640
- 'latitude' => $point->getLat(),
641
- 'longitude' => $point->getLng(),
642
- ]
643
- );
644
- } catch (\Throwable $e) {
645
- if (app()->bound('sentry')) {
646
- app('sentry')->captureException($e);
647
- }
648
- // create dummy $origin
649
- $origin = static::createObject(
650
- [
651
- 'latitude' => 0,
652
- 'longitude' => 0,
653
- ]
654
- );
655
- }
656
- }
657
-
658
- if ($destination instanceof \Fleetbase\FleetOps\Models\Place) {
659
- $destination = static::createObject(
660
- [
661
- 'latitude' => $destination->location->getLat(),
662
- 'longitude' => $destination->location->getLng(),
663
- ]
664
- );
665
- } else {
666
- try {
667
- $point = static::getPointFromMixed($destination);
668
- $destination = static::createObject(
669
- [
670
- 'latitude' => $point->getLat(),
671
- 'longitude' => $point->getLng(),
672
- ]
673
- );
674
- } catch (\Throwable $e) {
675
- if (app()->bound('sentry')) {
676
- app('sentry')->captureException($e);
677
- }
678
- // create dummy $destination
679
- $destination = static::createObject(
680
- [
681
- 'latitude' => 0,
682
- 'longitude' => 0,
683
- ]
684
- );
685
- }
686
- }
687
-
688
- $cacheKey = $origin->latitude . ':' . $origin->longitude . ':' . $destination->latitude . ':' . $destination->longitude;
689
-
690
- // check cache for results
691
- $cachedResult = Redis::get($cacheKey);
692
-
693
- if ($cachedResult) {
694
- $json = json_decode($cachedResult);
695
-
696
- if (!empty($json->distance) && !empty($json->time)) {
697
- return new DistanceMatrix($json->distance, $json->time);
698
- }
638
+ $origin = static::getPointFromMixed($origin);
639
+ $destination = static::getPointFromMixed($destination);
640
+ $origins = $origin->getLat() . ',' . $origin->getLng();
641
+ $destinations = $destination->getLat() . ',' . $destination->getLng();
642
+
643
+ // Create empty distance matric
644
+ $distanceMatrix = new DistanceMatrix(0, 0);
645
+ $provider = data_get($options, 'provider', config('fleetops.distance_matrix.provider'));
646
+ switch ($provider) {
647
+ case 'calculate':
648
+ $distanceMatrix = static::calculateDrivingDistanceAndTime($origins, $destinations);
649
+ break;
650
+
651
+ case 'osrm':
652
+ $distanceMatrix = static::getDistanceMatrixFromOSRM($origins, $destinations);
653
+ break;
654
+
655
+ case 'google':
656
+ default:
657
+ $distanceMatrix = static::getDistanceMatrixFromGoogle($origins, $destinations);
658
+ break;
699
659
  }
700
660
 
701
- $response = Http::get(
702
- 'https://maps.googleapis.com/maps/api/distancematrix/json',
703
- [
704
- 'origins' => $origin->latitude . ',' . $origin->longitude,
705
- 'destinations' => $destination->latitude . ',' . $destination->longitude,
706
- 'mode' => 'driving',
707
- 'key' => env('GOOGLE_MAPS_API_KEY'),
708
- ]
709
- )->json();
710
-
711
- $distance = data_get($response, 'rows.0.elements.0.distance.value');
712
- $time = data_get($response, 'rows.0.elements.0.duration.value');
713
-
714
- $result = static::createObject(
715
- [
716
- 'distance' => $distance,
717
- 'time' => $time,
718
- ]
719
- );
720
-
721
- // cache result
722
- Redis::set($cacheKey, json_encode($result));
723
-
724
- return new DistanceMatrix($distance, $time);
661
+ return $distanceMatrix;
725
662
  }
726
663
 
727
664
  /**
728
- * Calculates driving distance and time using Google distance matrix for multiple origins or destinations.
729
- * Returns distance in meters and time in seconds.
665
+ * Computes a distance matrix for multiple origins and destinations.
730
666
  *
731
- * @param Place|Point|array $origins
732
- * @param Place|Point|array $destinations
667
+ * This function is designed to handle arrays of origins and destinations to calculate a distance matrix.
668
+ * It converts the input arrays into a string format suitable for the map API request. The provider specified
669
+ * in the options (default is Google) determines which map service API is used for the calculations.
670
+ *
671
+ * @param array|\Illuminate\Support\Collection $origins array or collection of origin points
672
+ * @param array|\Illuminate\Support\Collection $destinations array or collection of destination points
673
+ * @param array $options options for API calls, like the provider and mode of transport
674
+ *
675
+ * @return DistanceMatrix an object representing the distance and time between the furthest origins and destinations
733
676
  */
734
- public static function distanceMatrix($origins = [], $destinations = []): DistanceMatrix
677
+ public static function distanceMatrix($origins = [], $destinations = [], $options = []): DistanceMatrix
735
678
  {
736
679
  if ($origins instanceof \Illuminate\Support\Collection) {
737
680
  $origins = $origins->all();
@@ -741,57 +684,83 @@ class Utils extends FleetbaseUtils
741
684
  $destinations = $destinations->all();
742
685
  }
743
686
 
744
- $origins = array_map(
745
- function ($origin) {
746
- $point = static::getPointFromMixed($origin);
747
- $origin = static::createObject(
748
- [
749
- 'latitude' => $point->getLat(),
750
- 'longitude' => $point->getLng(),
751
- ]
752
- );
753
-
754
- return $origin;
755
- },
756
- $origins
757
- );
687
+ // Convert origins and destinatinos to string
688
+ $origins = static::createCoordinateStringFromPlacesArray($origins);
689
+ $destinations = static::createCoordinateStringFromPlacesArray($destinations);
690
+
691
+ // Create empty distance matric
692
+ $distanceMatrix = new DistanceMatrix(0, 0);
693
+ $provider = data_get($options, 'provider', config('fleetops.distance_matrix.provider'));
694
+ switch ($provider) {
695
+ case 'calculate':
696
+ $distanceMatrix = static::calculateDrivingDistanceAndTime($origins, $destinations);
697
+ break;
698
+
699
+ case 'osrm':
700
+ $distanceMatrix = static::getDistanceMatrixFromOSRM($origins, $destinations);
701
+ break;
702
+
703
+ case 'google':
704
+ default:
705
+ $distanceMatrix = static::getDistanceMatrixFromGoogle($origins, $destinations);
706
+ break;
707
+ }
758
708
 
759
- $destinations = array_map(
760
- function ($destination) {
761
- $point = static::getPointFromMixed($destination);
762
- $destination = static::createObject(
763
- [
764
- 'latitude' => $point->getLat(),
765
- 'longitude' => $point->getLng(),
766
- ]
767
- );
768
-
769
- return $destination;
770
- },
771
- $destinations
772
- );
709
+ return $distanceMatrix;
710
+ }
773
711
 
774
- // get url ready string for origins
775
- $originsString = implode('|', array_map(
776
- function ($origin) {
777
- return $origin->latitude . ',' . $origin->longitude;
712
+ /**
713
+ * Converts an array of places into a coordinate string.
714
+ *
715
+ * Accepts an array of places (or any type that can be converted into geographical points)
716
+ * and converts each into a 'latitude,longitude' format. These coordinates are then concatenated into a single string,
717
+ * separated by the specified separator. The function gracefully handles any errors in the conversion process.
718
+ *
719
+ * @param array $places array of places to be converted into coordinates
720
+ * @param string $separator separator used to concatenate the coordinates into a single string
721
+ *
722
+ * @return string a concatenated string of coordinates
723
+ */
724
+ public static function createCoordinateStringFromPlacesArray(array $places = [], $separator = '|'): string
725
+ {
726
+ $coordinates = array_map(
727
+ function ($place) {
728
+ try {
729
+ $point = static::getPointFromMixed($place);
730
+ } catch (\Throwable $e) {
731
+ $point = new Point(0, 0);
732
+ }
733
+
734
+ return $point;
778
735
  },
779
- $origins
780
- ));
736
+ $places
737
+ );
781
738
 
782
- // get url ready string for origins
783
- $destinationString = implode('|', array_map(
784
- function ($destination) {
785
- return $destination->latitude . ',' . $destination->longitude;
739
+ return implode($separator, array_map(
740
+ function ($point) {
741
+ return $point->getLat() . ',' . $point->getLng();
786
742
  },
787
- $destinations
743
+ $coordinates
788
744
  ));
745
+ }
789
746
 
790
- $cacheKey = md5($originsString . '_' . $destinationString);
791
-
792
- // check cache for results
747
+ /**
748
+ * Retrieves a distance matrix using the Google Distance Matrix API.
749
+ *
750
+ * This function calculates the distance and time between origins and destinations using the Google API.
751
+ * It first checks if there is a cached result in Redis and, if not, performs an API call to Google.
752
+ * The results are then cached in Redis and returned as a DistanceMatrix object.
753
+ *
754
+ * @param string $origins a string of origins in 'latitude,longitude' format, separated by a specified delimiter
755
+ * @param string $destinations a string of destinations in a similar format to origins
756
+ * @param array $options additional options for the API call, like the mode of transport
757
+ *
758
+ * @return DistanceMatrix the distance and time between the given origins and destinations
759
+ */
760
+ public static function getDistanceMatrixFromGoogle(string $origins, string $destinations, $options = []): DistanceMatrix
761
+ {
762
+ $cacheKey = md5($origins . '_' . $destinations);
793
763
  $cachedResult = Redis::get($cacheKey);
794
-
795
764
  if ($cachedResult) {
796
765
  $json = json_decode($cachedResult);
797
766
 
@@ -803,9 +772,9 @@ class Utils extends FleetbaseUtils
803
772
  $response = Http::withOptions(['verify' => false])->get(
804
773
  'https://maps.googleapis.com/maps/api/distancematrix/json',
805
774
  [
806
- 'origins' => $originsString,
807
- 'destinations' => $destinationString,
808
- 'mode' => 'driving',
775
+ 'origins' => $origins,
776
+ 'destinations' => $destinations,
777
+ 'mode' => data_get($options, 'mode', 'driving'),
809
778
  'key' => env('GOOGLE_MAPS_API_KEY'),
810
779
  ]
811
780
  )->json();
@@ -813,17 +782,69 @@ class Utils extends FleetbaseUtils
813
782
  $distance = (float) data_get($response, 'rows.0.elements.0.distance.value', 0);
814
783
  $time = (float) data_get($response, 'rows.0.elements.0.duration.value', 0);
815
784
 
816
- $result = static::createObject(
817
- [
818
- 'distance' => $distance,
819
- 'time' => $time,
820
- ]
821
- );
785
+ // Create distance matrix
786
+ $distanceMatrix = new DistanceMatrix($distance, $time);
822
787
 
823
- // cache result
824
- Redis::set($cacheKey, json_encode($result));
788
+ // Cache distance matrix
789
+ Redis::set($cacheKey, json_encode($distanceMatrix));
825
790
 
826
- return new DistanceMatrix($distance, $time);
791
+ return $distanceMatrix;
792
+ }
793
+
794
+ /**
795
+ * Calculates distance and time between points using the OSRM API.
796
+ *
797
+ * Similar to the Google Distance Matrix function, this uses the OSRM (Open Source Routing Machine) API.
798
+ * It first checks for cached results before making a new API request. The origins and destinations are
799
+ * reformatted to the OSRM API requirement and the response is parsed into a DistanceMatrix object.
800
+ *
801
+ * @param string $origins comma-separated string of origin coordinates
802
+ * @param string $destinations comma-separated string of destination coordinates
803
+ *
804
+ * @return DistanceMatrix an object containing the distance and time for the specified journey
805
+ */
806
+ public static function getDistanceMatrixFromOSRM(string $origins, string $destinations): DistanceMatrix
807
+ {
808
+ $cacheKey = md5($origins . '_' . $destinations);
809
+ $cachedResult = Redis::get($cacheKey);
810
+ if ($cachedResult) {
811
+ $json = json_decode($cachedResult);
812
+
813
+ if (!empty($json->distance) && !empty($json->time)) {
814
+ return new DistanceMatrix($json->distance, $json->time);
815
+ }
816
+ }
817
+
818
+ // OSRM uses semi-colon to delimit coordinates so replace any `|` as we typically use with `;`
819
+ $origins = Str::replace('|', ';', $origins);
820
+ $destinations = Str::replace('|', ';', $destinations);
821
+ $coordinatesString = $origins . ';' . $destinations;
822
+ // Split the string into individual coordinates
823
+ $coordinatesArray = explode(';', $coordinatesString);
824
+
825
+ // Reverse the latitude and longitude in each coordinate
826
+ $reversedCoordinates = array_map(function ($coordinate) {
827
+ $parts = explode(',', $coordinate);
828
+ if (count($parts) == 2) {
829
+ return $parts[1] . ',' . $parts[0]; // Swap latitude and longitude
830
+ }
831
+
832
+ return $coordinate; // Return original if it's not a valid coordinate
833
+ }, $coordinatesArray);
834
+
835
+ // Join the reversed coordinates back into a string
836
+ $reversedCoordinatesString = implode(';', $reversedCoordinates);
837
+ $response = OSRM::getRouteFromCoordinatesString($reversedCoordinatesString);
838
+ $distance = (float) data_get($response, 'routes.0.distance', 0);
839
+ $time = (float) data_get($response, 'routes.0.duration', 0);
840
+
841
+ // Create distance matrix
842
+ $distanceMatrix = new DistanceMatrix($distance, $time);
843
+
844
+ // Cache distance matrix
845
+ Redis::set($cacheKey, json_encode($distanceMatrix));
846
+
847
+ return $distanceMatrix;
827
848
  }
828
849
 
829
850
  /**
@@ -883,13 +904,13 @@ class Utils extends FleetbaseUtils
883
904
  * https://en.wikipedia.org/wiki/Great-circle_distance#Formulas
884
905
  * https://en.wikipedia.org/wiki/Antipodal_point
885
906
  *
886
- * @param \Fleetbase\LaravelMysqlSpatial\Types\Point Starting point
887
- * @param \Fleetbase\LaravelMysqlSpatial\Types\Point Ending point
907
+ * @param Point Starting point
908
+ * @param Point Ending point
888
909
  * @param float $earthRadius Mean earth radius in [m]
889
910
  *
890
911
  * @return float Distance between points in [m] (same as earthRadius)
891
912
  */
892
- public static function vincentyGreatCircleDistance(\Fleetbase\LaravelMysqlSpatial\Types\Point $from, \Fleetbase\LaravelMysqlSpatial\Types\Point $to, float $earthRadius = 6371000): float
913
+ public static function vincentyGreatCircleDistance(Point $from, Point $to, float $earthRadius = 6371000): float
893
914
  {
894
915
  // convert from degrees to radians
895
916
  $latFrom = deg2rad($from->getLat());
@@ -910,7 +931,7 @@ class Utils extends FleetbaseUtils
910
931
  /**
911
932
  * Finds the nearest timezone for a given coordinate point.
912
933
  */
913
- public static function getNearestTimezone(\Fleetbase\LaravelMysqlSpatial\Types\Point $location, string $countryCode = ''): string
934
+ public static function getNearestTimezone(Point $location, string $countryCode = ''): string
914
935
  {
915
936
  $timezoneIds = \DateTimeZone::listIdentifiers(\DateTimeZone::PER_COUNTRY, $countryCode);
916
937
  $lat = $location->getLat();
@@ -1010,7 +1031,7 @@ class Utils extends FleetbaseUtils
1010
1031
 
1011
1032
  public static function isPoint($point): bool
1012
1033
  {
1013
- return $point instanceof \Fleetbase\LaravelMysqlSpatial\Types\Point;
1034
+ return $point instanceof Point;
1014
1035
  }
1015
1036
 
1016
1037
  /**
@@ -1275,7 +1296,7 @@ class Utils extends FleetbaseUtils
1275
1296
  *
1276
1297
  * @return float the heading in degrees from the starting point to the ending point
1277
1298
  */
1278
- public static function calculateHeading(\Fleetbase\LaravelMysqlSpatial\Types\Point $point1, \Fleetbase\LaravelMysqlSpatial\Types\Point $point2): float
1299
+ public static function calculateHeading(Point $point1, Point $point2): float
1279
1300
  {
1280
1301
  // Extract latitude and longitude from Point objects
1281
1302
  $lat1 = $point1->getLat();
@@ -12,9 +12,6 @@ use Illuminate\Support\Facades\Route;
12
12
  | is assigned the "api" middleware group. Enjoy building your API!
13
13
  |
14
14
  */
15
-
16
- Route::get('fleet-ops-test', 'Fleetbase\FleetOps\Http\Controllers\Controller@test');
17
-
18
15
  Route::prefix(config('fleetops.api.routing.prefix', null))->namespace('Fleetbase\FleetOps\Http\Controllers')->group(
19
16
  function ($router) {
20
17
  /*