In this tutorial, learn how you can create realtime map using Laravel, Angular, Google Maps and Pusher. The map updates when new location is pushed to it.
Realtime maps have become very popular with more and more couriers, delivery and transportation services apps using them to show realtime tracking of your order or available vehicles around you. Today we will learn how to build a realtime map using Laravel and Pusher.
To get started you need to sign up to a free Pusher account here. After you have created your account and are logged into the Pusher dashboard, click the Create new App
button. Name your new app “Laravel-Map” and select a cluster. A cluster represents the physical location of the servers that handle requests from your app, therefore selecting a cluster closer to your location will mean faster response.
Next select the front-end and back-end technologies – in this case, Angular and Laravel. (If you want, you can also give a short description of what app you’re building.)
When you’ve done, click the “Create my app” button.
Next, navigate to the “App Keys” tab on the dashboard to see your application keys. Note your application keys because we will be using them later in this tutorial.
Navigate into the directory where you would like your apps to be and install Laravel via composer by running the following command in your terminal.
composer create-project --prefer-dist laravel/laravel laravel-map
Add pusher-php-server to the required dependencies of your composer.json file so that it looks similar to this.
1"require": { 2 "php": ">=5.6.4", 3 "laravel/framework": "5.4.*", 4 "laravel/tinker": "~1.0", 5 "pusher/pusher-php-server": "^2.2" 6 },
The pusher-php-server is a PHP library which will help us broadcast events to Pusher which will then be listened to by our client side. To install it, run the following command
composer install
After installation completes, rename the .env.example
file to .env
and add the keys you got from Pusher to their respective places in the file. Also set the BROADCAST_DRIVER
to pusher
and add a PUSHER_APP_CLUSTER
key with its value set to your Pusher apps cluster. It should look similar to the following:
1BROADCAST_DRIVER=pusher 2PUSHER_APP_ID=XXXXX 3PUSHER_APP_KEY=XXXXXXXXXXXXXXXXX 4PUSHER_APP_SECRET=XXXXXXXXXXXXXXXXXX 5PUSHER_APP_CLUSTER=XXXXXXXXXX
Next, open the config/broadcasting.php
file and set the Pusher cluster option like this:
1<?php 2 3return [ 4 5 ... 6 7 'connections' => [ 8 9 'pusher' => [ 10 'driver' => 'pusher', 11 'key' => env('PUSHER_APP_KEY'), 12 'secret' => env('PUSHER_APP_SECRET'), 13 'app_id' => env('PUSHER_APP_ID'), 14 'options' => [ 15 //add this line 16 'cluster' => env('PUSHER_APP_CLUSTER'), 17 ], 18 ], 19 20 ...
Next, run the following command to generate a Laravel secret key.
php artisan key:generate
We will be using a public channel to make this tutorial as simple as possible, so we won’t be changing anything else.
Next, let us create a SendLocation event. To do this, run the following Laravel command:
php artisan make:event SendLocation
You should see a PHP file in your app/Events
directory named SendLocation.php
. The only thing we will be changing in the file is the channel-name. We will also implement the ShouldBroadcast interface and add a public variable to it. The complete code should look similar to the following:
1<?php 2 3namespace App\Events; 4 5use Illuminate\Broadcasting\Channel; 6use Illuminate\Queue\SerializesModels; 7use Illuminate\Foundation\Events\Dispatchable; 8use Illuminate\Broadcasting\InteractsWithSockets; 9use Illuminate\Contracts\Broadcasting\ShouldBroadcast; 10 11class SendLocation implements ShouldBroadcast 12{ 13 use Dispatchable, InteractsWithSockets, SerializesModels; 14 15 /** 16 * Create a new event instance. 17 * 18 * @return void 19 */ 20 public $location; 21 22 public function __construct($location) 23 { 24 $this->location = $location; 25 } 26 27 /** 28 * Get the channels the event should broadcast on. 29 * 30 * @return Channel|array 31 */ 32 public function broadcastOn() 33 { 34 return new Channel('location'); 35 } 36}
Finally, create an endpoint where coordinates will be sent. When requests are made to the endpoint, the SendLocation
event will be triggered and the coordinates will be sent to Pusher. We’ll do that in our web.php file located in the routes
folder. Add the code below the file:
1Route::post('/map', function (Request $request) { 2 $lat = $request->input('lat'); 3 $long = $request->input('long'); 4 $location = ["lat"=>$lat, "long"=>$long]; 5 event(new SendLocation($location)); 6 return response()->json(['status'=>'success', 'data'=>$location]); 7});
We will be using Google Maps to implement our realtime map. This guide will run you through registering a project in the Google API Console and activating the Google Maps JavaScript API. Remember to grab the API key that will be generated for you after registering.
We’ll be using Angular for our front-end. If you don’t have angular installed, run the following command:
npm install -g @angular/cli
Now create an Angular app with the following command:
ng new angular-map
Next we will install dependencies to listen to events sent to Pusher by our server. Pusher has a JavaScript library for front-end technologies which we’ll be using to listen for events from Pusher.
We’ll also be installing Laravel Echo. To do this, navigate into the “angular-map” project and run the following command:
npm install --save laravel-echo pusher-js
Now that we have installed the dependencies, let’s get to the code. In your index.html file, add Pusher and Google Maps scripts. Your index.html file should look similar to the code snippet below:
1<!doctype html> 2<html> 3<head> 4 <title>Simple Map</title> 5 <meta name="viewport" content="initial-scale=1.0"> 6 <meta charset="utf-8"> 7 8</head> 9<body style="margin: 0"> 10 11<script src="https://js.pusher.com/3.0/pusher.min.js"></script> 12<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_GOOGLE_APP_KEY"></script> 13 <app-root>Loading...</app-root> 14 15</body> 16</html>
We need somewhere to render the map, so add the following line to your app.component.html file:
<div id="map"></div>
Finally, we move over to our app.component.ts file to add the code which will render Google Maps on our HTML page.
1import { Component, OnInit } from '@angular/core' 2import * as Echo from 'laravel-echo'; 3 4declare var google:any; 5 6const PUSHER_API_KEY = 'YOUR_PUSHER_KEY'; 7const PUSHER_CLUSTER = 'YOUR_PUSHER_CLUSTER'; 8 9@Component({ 10 selector: 'app-root', 11 templateUrl: './app.component.html', 12 styleUrls: ['./app.component.css'] 13}) 14 15export class AppComponent implements OnInit{ 16 17 lat : number = 9.0820; 18 long : number = 8.6753; 19 20 ngOnInit() { 21 22 this.launchMap(this.lat, this.long); 23 24 } 25 26 27 launchMap(lat, lng){ 28 var nigeria= {lat: lat, lng: lng}; 29 this.map = new google.maps.Map(document.getElementById('map'), { 30 zoom: 8, 31 center: nigeria 32 }); 33 this.marker = new google.maps.Marker({ 34 map: this.map, 35 animation:"bounce", 36 }); 37 this.lineCoordinates.push(new google.maps.LatLng(this.lat, this.long)); 38 } 39}
Next we add the code which listens to the event and updates the map when the coordinates change.
1subscribe(){ 2 var echo = new Echo({ 3 broadcaster: 'pusher', 4 key: PUSHER_API_KEY, 5 cluster: PUSHER_CLUSTER 6 }); 7 echo.channel('location') 8 .listen('SendLocation', (e)=>{ 9 this.data = e.location; 10 this.updateMap(this.data); 11 }); 12 } 13 14updateMap(data){ 15 this.lat = parseFloat(data.lat); 16 this.long = parseFloat(data.long); 17 18 this.map.setCenter({lat:this.lat, lng:this.long, alt:0}); 19 this.marker.setPosition({lat:this.lat, lng:this.long, alt:0}); 20 21 this.lineCoordinates.push(new google.maps.LatLng(this.lat, this.long)); 22 23 var lineCoordinatesPath = new google.maps.Polyline({ 24 path: this.lineCoordinates, 25 geodesic: true, 26 map: this.map, 27 strokeColor: '#FF0000', 28 strokeOpacity: 1.0, 29 strokeWeight: 2 30 });
The complete code snippet for the app.component.ts file:
1import { Component, OnInit } from '@angular/core'; 2import * as Echo from 'laravel-echo'; 3import * as Pusher from 'pusher-js'; 4 5declare let google:any; 6 7const PUSHER_API_KEY = 'YOUR_PUSHER_KEY'; 8const PUSHER_CLUSTER = 'YOUR_PUSHER_CLUSTER'; 9 10@Component({ 11 selector: 'app-root', 12 templateUrl: './app.component.html', 13 styleUrls: ['./app.component.css'] 14}) 15 16export class AppComponent implements OnInit{ 17 data : any; 18 map : any; 19 lat : number = 9.0820; 20 long : number = 8.6753; 21 marker : any; 22 lineCoordinates = []; 23 24 ngOnInit() { 25 26 this.subscribe(); 27 this.launchMap(this.lat, this.long); 28 29 } 30 31 32 subscribe(){ 33 let echo = new Echo({ 34 broadcaster: 'pusher', 35 key: PUSHER_API_KEY, 36 cluster: PUSHER_CLUSTER 37 }); 38 echo.channel('location') 39 .listen('SendLocation', (e)=>{ 40 this.data = e.location; 41 this.updateMap(this.data); 42 }); 43 } 44 45 launchMap(lat, lng){ 46 let nigeria= {lat: lat, lng: lng}; 47 this.map = new google.maps.Map(document.getElementById('map'), { 48 zoom: 14, 49 center: nigeria 50 }); 51 this.marker = new google.maps.Marker({ 52 map: this.map, 53 animation:"bounce", 54 }); 55 this.lineCoordinates.push(new google.maps.LatLng(this.lat, this.long)); 56 } 57 58 updateMap(data){ 59 this.lat = parseFloat(data.lat); 60 this.long = parseFloat(data.long); 61 62 this.map.setCenter({lat:this.lat, lng:this.long, alt:0}); 63 this.marker.setPosition({lat:this.lat, lng:this.long, alt:0}); 64 65 this.lineCoordinates.push(new google.maps.LatLng(this.lat, this.long)); 66 67 let lineCoordinatesPath = new google.maps.Polyline({ 68 path: this.lineCoordinates, 69 geodesic: true, 70 map: this.map, 71 strokeColor: '#FF0000', 72 strokeOpacity: 1.0, 73 strokeWeight: 2 74 }); 75 } 76}
Here is a gif showing the map being updated in realtime:
In order to see the marker move on the map, you will need to send App\Events\SendLocation
events to the location
channel. The easiest way to do this is by using the event creator on the Pusher Debug Console. Here is a sample data format that can be used to trigger an update:
1{ 2 "location": { 3 "lat": "9.084999999999999", 4 "long": "8.678299999999998" 5 } 6}
Here is an image of how the event would look on the Pusher event creator:
Alternatively, location updates can also be triggered by sending web requests to the Laravel application. Here is an Angular function that can be used to send location update requests:
1sendLocation(lat: string, long: string) { 2 const serverUrl = 'http://localhost:8000'; 3 const params = new URLSearchParams(); 4 params.set("lat", lat); 5 params.set("long", long); 6 7 return this.http.post(serverUrl + '/map', params); 8}
Note that the above function is assuming that the Laravel application is accessible via ‘localhost:8000’, you can update the serverUrl
to your own configuration.
We have successfully created a real time map which will be updated when new coordinates are sent to the endpoint on our server. You could use this to track the location of almost anything – but remember, if you are using this in production, you should consider privacy needs and build that functionality in.
You can find the complete project in these repositories angular app and laravel app.