- Share on
Display geolocated items on a google map with Ionic
November 2019
Ok to display our bikes on a google map, we need to add a new ionic page that we will call : ShowBikes
ionic g page ShowBikes
and as usual, we will move the new directory created show-bikes into the pages directory, and adapt the app-routing.mobule.ts file to reflect the change
{ path: 'show-bikes', loadChildren: './pages/show-bikes/show-bikes.module#ShowBikesPageModule' }
Now we want to geolocate our user to be able to call our new django api endpoint. So we need to install some new plugins and this to our ionic application
ionic cordova plugin add cordova-plugin-geolocation
npm install @ionic-native/geolocation
ionic cordova plugin add cordova-plugin-request-location-accuracy
npm install @ionic-native/location-accuracy
next modify our app.module.ts to import and initialize this library
import { Geolocation } from '@ionic-native/geolocation/ngx';
import { LocationAccuracy } from '@ionic-native/location-accuracy/ngx';
@NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [BrowserModule,
IonicModule.forRoot(),
IonicStorageModule.forRoot(),
HttpClientModule,
AppRoutingModule],
providers: [
StatusBar,
SplashScreen,
Network,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
ApiDjangoService,
Geolocation,
LocationAccuracy
],
bootstrap: [AppComponent]
})
export class AppModule {}
We added the Geolocation module into the providers list.
Now we can edit our show-bikes.pages.ts to import some of the libraries that we will need
import { Component, OnInit } from '@angular/core';
import { Geolocation,Geoposition } from '@ionic-native/geolocation/ngx';
import { Platform } from '@ionic/angular';
import { ApiDjangoService } from '../../services/api-django.service';
import { Router } from '@angular/router';
import { LocationAccuracy } from '@ionic-native/location-accuracy/ngx';
@Component({
selector: 'app-show-bikes',
templateUrl: './show-bikes.page.html',
styleUrls: ['./show-bikes.page.scss'],
})
export class ShowBikesPage implements OnInit {
constructor(public geolocation: Geolocation,
public apiService: ApiDjangoService,
public platform : Platform,
public locac : LocationAccuracy,
public router: Router) {
}
ngOnInit() {
}
}
If you remember the tutorial for login and registering users, we do nothing after logged in or registered successfully. It is time to redirect the end user to this new page show-bikes
this.router.navigateByUrl("/show-bikes")
Ok now we need to write the method geoloc which will geolocate the user
geoloc() {
return new Promise(resolve => {
if (this.platform.is('cordova')) {
let options = {
enableHighAccuracy: true,
maximumAge:0,
timeout:10000
};
console.log("------ PLATFORM CORDOVA");
this.locac.request(this.locac.REQUEST_PRIORITY_HIGH_ACCURACY).then(() => {
this.geolocation.getCurrentPosition(options).then((position: Geoposition) => {
console.log("============= POSITION ================");
console.log(position)
resolve(position);
}).catch((err) => {
console.log("Error GEOLOC " + JSON.stringify(err))
resolve(false)
})
});
}
else{
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
position => {
console.log("============= POSITION ================");
console.log(position)
//Hardcoded
resolve(position);
},
error => {
resolve(false);
}
);
}
}
})
}
First, we check if user is on a mobile or using a browser.
On mobile, we can use the LocationAccuracy to get from the device the best accuracy possible, and then we ask the device to get it's current position.
Otherwise we use the navigator.geolocation method to get the position
We can now call this method on our constructor
constructor(public geolocation: Geolocation,
public apiService: ApiDjangoService,
public platform : Platform,
public locac : LocationAccuracy,
public router: Router) {
this.apiService.showLoading()
this.geoloc().then((position)=>{
this.apiService.stopLoading()
this.positionUser = position
if (position){
this.displayData()
}
else{
this.apiService.showError("Sorry unable to geolocate")
}
})
}
If we have a position we call our django backend api to get bikes around otherwise we display a message to the user that is position has not been retrieved.
Let's write our api method to retrieve bikes from backend
getBikesAround(latitude,longitude){
const options = {
headers: new HttpHeaders({
'Authorization': 'Bearer ' + this.tokenSSO,
'Content-Type': 'application/json'
})
};
// distance is 1000 Km for example, please modify to your requirements
let url = this.getBikesAroundUrl+"?latitude="+latitude+"&longitude="+longitude+"&max_distance=1000"
return Observable.create(observer => {
// At this point make a request to your backend
this.http.get(url, options)
.pipe(retry(1))
.subscribe(res => {
observer.next(res);
observer.complete();
}, error => {
observer.next();
observer.complete();
console.log(error);// Error getting the data
});
});
}
Ok now before writing our displayData() method, we will configure our Ionic project to be able to use Google maps.
You need to have a valid Google Map Api Key, have activated the maps library in your console, then you need to add this API KEY in your index.html file
<script src="https://maps.googleapis.com/maps/api/js?v=3&key=YOURAPIKEY;libraries=maps"></script>
<script src="https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/markerclusterer.js"></script>
I have also included the markercluster javascript library because we will group our bikes using cluster. The idea is to avoid displaying a lot of bikes on the map, and to group them. Please consult the MarkerCluster api documentation, to better understand the concept.
Now we will add the import and new variables needed in our ionic code.
import { Component, OnInit,ViewChild } from '@angular/core';
import { ElementRef } from '@angular/core';
...
export class ShowBikesPage implements OnInit {
positionUser : any;
bikesList : any;
markersBike : any;
markerCluster : any;
@ViewChild('map') mapElement: ElementRef;
map: any;
We will write our html
<ion-header>
<ion-toolbar>
<ion-title>ShowBikes</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<div #map id="map" class="map"></div>
</ion-content>
and our scss Don't forget the scss otherwise the google map will not be displayed.
.map {
height: 100%;
}
Ok we have setup everything and can write the displayData() method
displayData(){
let latitude = this.positionUser.coords.latitude
let longitude = this.positionUser.coords.longitude
this.apiService.showLoading()
this.apiService.getBikesAround(latitude,longitude).subscribe((data)=>{
console.log(data)
this.apiService.stopLoading()
if (data){
let nb = data["count"]
if (nb>0){
this.markersBike = []
let latLng = new google.maps.LatLng(this.positionUser.coords.latitude, this.positionUser.coords.longitude);
let mapOptions = {
center: latLng,
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
this.map = new google.maps.Map(this.mapElement.nativeElement, mapOptions);
for (let bike of data["results"]){
console.log(JSON.stringify(bike))
let location = bike.location;
let coordinates = location.coordinates
let currentLatitude = coordinates[1];
let currentLongitude = coordinates[0];
let urlPicture: string;
urlPicture = 'assets/imgs/bike.jpg'
var image = {
url: urlPicture,
size: new google.maps.Size(40, 40),
scaledSize: new google.maps.Size(40, 40)
};
let latLng = new google.maps.LatLng(currentLatitude, currentLongitude);
let marker = new google.maps.Marker({
map: this.map,
icon: image,
animation: google.maps.Animation.DROP,
position: latLng
});
this.markersBike.push(marker)
}
let styles_marker = [{
url: 'assets/imgs/pictogroupegardemanger.png',
height: 40,
width: 40,
anchor: [0, -1],
textColor: '#010A72',
textSize: 11
}];
this.markerCluster = new MarkerClusterer(this.map, this.markersBike,
{ imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m' });
}
else{
this.apiService.showMessage("Sorry","No bikes around you")
}
}
})
}
By default the map will center on our user position.