
¿Pensando en incluir algún sistema de geolocalización en tu aplicación? O, quizás, ya lo has hecho, pero te falta conocer algún detalles cómo los permisos o encender o apagar el GPS ¿verdad
Si has respondido que sí a alguna de las dos preguntas, tranqui. No eres el primero, ni serás el último, que pasa por aquí a intentar darles respuesta. Y lo consigue.
Mi nombre es Aitor Sánchez, soy desarrollador de apps desde 2014, y en este artículo te enseñaré todo lo que necesitas saber sobre la geolocalización en tu aplicación de Ionic.
Pero antes de continuar, este es El Círculo. Es mi newsletter donde te puedo enseñar desarrollo de apps móviles, aso y monetización. Por cierto, si te suscribes te regalo mi ebook Duplica los ingreso de tus apps en 5 minutos. No es broma.
P.D: Darse de alta es gratis y de baja, también.
Consultar estructura del artículo
- 1 Entorno de desarrollo del tutorial
- 2 1) Creamos una aplicación con capacitor
- 3 2) Agregamos el plugin de Google Maps y otros necesarios
- 4 4) Implementando la solicitud de permisos
- 5 5) Implementando el auto GPS
- 6 6) Implementando la localización y mostrar la posición del usuario sobre un mapa
- 7 7) Compilar en un dispositivo y probar (Android)
Entorno de desarrollo del tutorial
Node 12.14.1 (cualquier versión 12.x funcionaría)
Ionic 5 (CLI 5.3.0)
Capacitor 2.0
Angular ~9.1.x
1) Creamos una aplicación con capacitor
El primer, es confirmar que tenemos la última versión de Ionic CLI.
En caso de que no la tengas, ejecuta el siguiente comando:
... $ npm install -g [email protected] ...
Esto instalará, o actualizará, Ionic a la última versión disponible (dentro del paquete de Ionic ya está el CLI).
Una vez terminado de ejecutar el comando anterior, creamos un nuevo proyecto de app con el siguiente comando:
... $ ionic start IonicGeo blank --type=angular --capacitor ...
Flags:
- –type=angular: Define que tipo de FW vamos a utilizar para hacer la aplicación. En este caso, Angular, que es el FW que utilizaremos para hacer el tutorial.
- –capacitor: Definimos que queremos utilizar Capacitor cómo puente con la parte nativa del sistema donde se ejecute la app. ¡Importante: no definir «Cordova» aquí o dejarlo vacio!!
- blank: Define que queremos una aplicación vacía, solo con una página en blanco cómo pantalla principal.
Probamos que todo funcione correctamente con el siguiente comando:
... $ ionic serve ...
Si todo ha ido bien, se mostrará la aplicación. Si no existen errores, continuaremos.
Si te ha dado algún fallo este paso, dímelo en los comentarios y vemos a ver cómo podemos arreglarlo.
2) Agregamos el plugin de Google Maps y otros necesarios
Para poder hacer este tutorial necesitaremos los siguientes plugins/paquetes.
- Paquete de Google Maps.
- Plugin de permisos de geolocalización.
- Plugin para controlar el GPS.
- Plugin de geolocalización.
2.1 Agregamos el paquete de Google Maps
Para esto, vamos a utilizar el siguiente comando:
... $ npm install @agm/core ...
2.2 Ahora el plugin para los permisos de Geolocalización
Recordemos que este plugin solo es necesario para Android.
... $ npm install cordova-plugin-android-permissions $ npm install @ionic-native/android-permissions ...
2.3 Plugin para controlar el GPS
Este plugin permitirá al usuario encender/apagar el GPS sin necesidad de tener que salir de la aplicación.
¿Cómo lo hace el plugin? Mediante un modal que se lo solicitará cuando nosotros queramos que lo haga.
Para instalarlo:
... $ npm install cordova-plugin-request-location-accuracy $ npm install @ionic-native/location-accuracy ...
2.4 Plugin de Geolocalización
A diferencia de cuando utilizamos Cordova, que necesitamos instalar el Plugin aparte, Capacitor ya viene de serie con él. Así que no tendremos que hacer nada. Podríamos utilizarlo directamente así:
... import { Plugins} from "@capacitor/core"; const { Geolocation} = Plugins; ...
Por fin, ya estamos en disposición de importar los plugins y las funcionalidad para comenzar a utilizar todo esto.
3) Implementamos Google Maps
Recordemos que el modelo estándar de construcción de aplicaciones móviles con Angular define una estructura de componentes con los siguientes archivos:
- .scss: Para el estilo del componente.
- .modelu.ts: Para los imports, declarations, etc… (digamos, la activación)
- .page.ts: Para el código y la lógica del componente.
- .html: La vista.
En la aplicación de ejemplo que hemos creado, tendremos una página por defecto llamada «home» que tendrá ya disponibles los archivos que hemos mencionado.
El primero paso de la implementación de Google Maps, es importa el módulo «AgmCoreModule» en el archivo «app.module.ts» de la siguiente manera:
... import { AgmCoreModule } from '@agm/core'; ... @NgModule({ ... imports: [ .... AgmCoreModule.forRoot({ apiKey: 'YOUR_GOOGLE_MAPS_API_KEY', }), ], .... }) ...
Nota: Cómo es lógico, donde utlizamos «YOUR_GOOGLE_MAPS_API_KEY» tendrás que introducir tu KEY de la api de Google Maps.
Después de hacer esto, tenemos que importar «AgmCoreModule» en el archivo «home.module.ts». Al hacerlo nos permitirá utilizar el módulo desde nuestra página «Home». Lo haremos así:
import { AgmCoreModule } from '@agm/core'; ... @NgModule({ imports: [ ... AgmCoreModule, ... ], })
Probemos todo antes de continuar. En «home.page.html» vamos a agregar el siguiente código HTML:
<agm-map [latitude]="lat" [longitude]="lng" [zoom]="15" [disableDefaultUI]="false"> <agm-marker [latitude]="lat" [longitude]="lng" [markerDraggable]=true> </agm-marker> </agm-map>
Y en tu archivo «home.page.ts» vamos a definir las dos variables que hemos utilizado en este código. La variable «lat» y la variable «lng».
Ahora, si ejecutas la aplicación tal cual, y has seguido todos los pasos al pie de la letra, tendrás algo así:
4) Implementando la solicitud de permisos
Vamos a crear una directorio dentro de «app» que se llamará services. Será el encargado de contener todos los servicios de nuestra aplicación.
Siguiendo el orden, y dentro de services, vamos a crear un archivo que se va a llamar «location.service.ts» en el que programaremos el servicio encargado de las cosas de la localización.
Pitaría así, más o menos:
import { LocationAccuracy } from '@ionic-native/location-accuracy'; import { Capacitor } from "@capacitor/core"; ... // Comprobar si la aplicación tienes permisos para acceder a la información del GPS. async checkGPSPermission(): Promise<boolean> { return await new Promise((resolve, reject) => { if (Capacitor.isNative) { AndroidPermissions.checkPermission(AndroidPermissions.PERMISSION.ACCESS_FINE_LOCATION).then( result => { if (result.hasPermission) { // Tenemos el permiso concedido. resolve(true); } else { //No tenemos permiso, solicitarlo mediante el modal. resolve(false); } }, err => {alert(err);} );} else {resolve(true); } }) } async requestGPSPermission(): Promise<string> { return await new Promise((resolve, reject) => { LocationAccuracy.canRequest().then((canRequest: boolean) => { if (canRequest) { resolve('CAN_REQUEST'); } else { // Mostramos el modal de solicitud de permisos. AndroidPermissions.requestPermission(AndroidPermissions.PERMISSION.ACCESS_FINE_LOCATION) .then( (result) => { if (result.hasPermission) { // Llamamos la método para encender el GPS. resolve('GOT_PERMISSION'); } else { resolve('DENIED_PERMISSION'); } }, error => { // Mostramos un alert si el usuario ha hecho click en "no". alert('requestPermission Error requesting location permissions ' + error); }); } }); }) }
Bien, podrás utilizar esta función nada más cargar la aplicación, o justo antes de cuando se vaya a utilizar. Eso ya depende más de ti que de mí. Pero te aconsejo que lo hagas solo cuando la vayas a utilizar.
5) Implementando el auto GPS
En el mismo archivo «location.service.ts» que hemos creado antes, y a continuación de lo que hemos programado, vamos a colocar el siguiente código:
import { Injectable } from '@angular/core'; import { LocationAccuracy } from '@ionic-native/location-accuracy'; import { Capacitor } from "@capacitor/core"; @Injectable({ providedIn: 'root' }) export class LocationService { constructor() { } async askToTurnOnGPS(): Promise<boolean> { return await new Promise((resolve, reject) => { LocationAccuracy.canRequest().then((canRequest: boolean) => { if (canRequest) { // the accuracy option will be ignored by iOS LocationAccuracy.request(LocationAccuracy.REQUEST_PRIORITY_HIGH_ACCURACY).then( () => { resolve(true); }, error => { resolve(false); } ); } else {resolve(false);} }); }) } }
La elección de cómo colocarlo, el código del punto anterior y este, lo dejo a tu elección también. Lo importante es que esté. Y en el orden en el que está.
Porque si te fijas, el decorador «injectable» tiene que estar justo en las líneas anteriores a la creación de la clase «LocationService». Si no está así, no funcionará nada.
¿Qué hará la función askToTurnOnGPS? Pues cómo su nombre indica, le preguntará al sistema si podemos utilizar le GPS.
6) Implementando la localización y mostrar la posición del usuario sobre un mapa
Ahora, deberás de utilizar la función «watchPosition» para trackear, de manera continua, la posición en la que se encuentra el dispositivo donde se está ejecutando la aplicación. Y, por extensión, el usuario.
Nota: Recuerda, esta funcionalidad consume bastante batería, recuerda limpiar el tracker llamando a «clearWatch» cuando vayas a dejar de utilizarlo en una aplicación real.
Perfecto, ahora combinaremos todo el código que hemos visto en la parte de arriba de este artículo para poner todo en marcha.
Comenzamos:
«home.page.html»
<ion-header> <ion-toolbar color="primary"> <ion-title> Geolocation </ion-title> </ion-toolbar> </ion-header> <ion-content> <agm-map [latitude]="lat" [longitude]="lng" [zoom]="15" [disableDefaultUI]="false"> <agm-marker [latitude]="lat" [longitude]="lng" [markerDraggable]=true></agm-marker> </agm-map> <ion-row> <ion-col>Latitude: {{lat}}</ion-col> <ion-col>Longitude: {{lng}}</ion-col> </ion-row> </ion-content> <ion-footer> <ion-button expand="full" color="primary" (click)="getMyLocation()">Get My Location</ion-button> </ion-footer>
«home.page.ts»
import { Component, NgZone } from '@angular/core'; import { Capacitor, Plugins } from "@capacitor/core"; import { LocationService } from '../location.service'; const { Geolocation, Toast } = Plugins; @Component({ selector: 'app-home', templateUrl: 'home.page.html', styleUrls: ['home.page.scss'], }) export class HomePage { lat: any; lng: any; watchId: any; constructor(public ngZone: NgZone, private locationService: LocationService) { this.lat = 12.93448; this.lng = 77.6192; } async getMyLocation() { const hasPermission = await this.locationService.checkGPSPermission(); if (hasPermission) { if (Capacitor.isNative) { const canUseGPS = await this.locationService.askToTurnOnGPS(); this.postGPSPermission(canUseGPS); } else { this.postGPSPermission(true); } } else { const permission = await this.locationService.requestGPSPermission(); if (permission === 'CAN_REQUEST' || permission === 'GOT_PERMISSION') { if (Capacitor.isNative) { const canUseGPS = await this.locationService.askToTurnOnGPS(); this.postGPSPermission(canUseGPS); } else { this.postGPSPermission(true); } } else { await Toast.show({ text: 'User denied location permission' }) } } } async postGPSPermission(canUseGPS: boolean) { if (canUseGPS) { this.watchPosition(); } else { await Toast.show({ text: 'Please turn on GPS to get location' }) } } async watchPosition() { try { this.watchId = Geolocation.watchPosition({}, (position, err) => { this.ngZone.run(() => { if (err) { console.log('err', err); return; } this.lat = position.coords.latitude; this.lng = position.coords.longitude this.clearWatch(); }) }) } catch (err) { console.log('err', err) } } clearWatch() { if (this.watchId != null) { Geolocation.clearWatch({ id: this.watchId }); } } }
«location.service.ts»
import { Injectable } from '@angular/core'; import { AndroidPermissions } from '@ionic-native/android-permissions'; import { LocationAccuracy } from '@ionic-native/location-accuracy'; import { Capacitor } from "@capacitor/core"; @Injectable({ providedIn: 'root' }) export class LocationService { constructor() { } async askToTurnOnGPS(): Promise<boolean> { return await new Promise((resolve, reject) => { LocationAccuracy.canRequest().then((canRequest: boolean) => { if (canRequest) { // the accuracy option will be ignored by iOS LocationAccuracy.request(LocationAccuracy.REQUEST_PRIORITY_HIGH_ACCURACY).then( () => { resolve(true); }, error => { resolve(false); } ); } else { resolve(false); } }); }) } // Check if application having GPS access permission async checkGPSPermission(): Promise<boolean> { return await new Promise((resolve, reject) => { if (Capacitor.isNative) { AndroidPermissions.checkPermission(AndroidPermissions.PERMISSION.ACCESS_FINE_LOCATION).then( result => { if (result.hasPermission) { // If having permission show 'Turn On GPS' dialogue resolve(true); } else { // If not having permission ask for permission resolve(false); } }, err => { alert(err); } ); } else { resolve(true); } }) } async requestGPSPermission(): Promise<string> { return await new Promise((resolve, reject) => { LocationAccuracy.canRequest().then((canRequest: boolean) => { if (canRequest) { resolve('CAN_REQUEST'); } else { // Show 'GPS Permission Request' dialogue AndroidPermissions.requestPermission(AndroidPermissions.PERMISSION.ACCESS_FINE_LOCATION) .then( (result) => { if (result.hasPermission) { // call method to turn on GPS resolve('GOT_PERMISSION'); } else { resolve('DENIED_PERMISSION'); } }, error => { // Show alert if user click on 'No Thanks' alert('requestPermission Error requesting location permissions ' + error); } ); } }); }) } }
«home.page.scss»
#map { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; } agm-map { height: calc(100vh - 164px); } agm-map { .gm-svpc { display: none; } } ion-row { height: 64px; align-items: center; background: black; color: white; ion-col { text-align: center; } }
7) Compilar en un dispositivo y probar (Android)
Antes de que instalemos la aplicación en nuestro dispositivo, lo que tenemos que hacer es compilar los recursos. Para ello vamos a utilizar el siguiente comando:
... $ ionic build ...
Agregamos la plataforma de Android a nuestro proyecto:
... $ npx cap add android ...
En caso de que no usemos Android Studio cómo IDE, hacemos esto:
... $ npx cap open android ...
Esto abrirá Android Studio y cargará este proyecto sobre él.
Selecciona el dispositivo donde quieres cargar la aplicación. Ya sea en un emulador, o en un dispositivo físico, y dale al play. Verás algo como esto:
Bueno, pues esto ha sido todo por hoy en la guía más completa que encontrarás sobre Google Maps en Ionic en la red.
Solo queda despedirme. Así que nos vemos en el siguiente artículo. Hasta entonces ¡que vaya bien!