Local blog for Spanish speaking developers in LATAM
Profundizando en un stack tecnológico para seguimiento en tiempo real utilizando Google Maps, Angular y Firebase
jueves, 28 de mayo de 2020
Las aplicaciones de tipo “Uber Like” (como Uber) o “Rappi Like” se han venido popularizando. Una gran cantidad de soluciones tecnológicas hoy en día tratan de emular el comportamiento o la funcionalidad de este tipo de aplicaciones. Esto se debe a que algunas compañías empiezan a incursionar en el mundo del delivery o los ecommerce y tratan de atarse a casos de éxito ya establecidos. Para nadie es un secreto que es increíble poder ver al conductor de Uber en tiempo real acercarse a nuestra ubicación y poder estar preparados casi con exactitud para subirnos al vehículo. De igual manera, cuando ordenamos un domicilio a algún restaurante o a alguna farmacia nos gusta saber siempre cuando la persona encargada de recoger el domicilio lo hizo y poder visualizar dónde está en cada momento. Por eso, en este artículo les enseñaremos cómo funcionan las aplicaciones que hacen seguimiento en tiempo real de un objeto y aprenderemos a visualizar utilizando
Google Maps
,
Angular
y
Firebase
.
Empecemos por lo básico. En este tipo de aplicaciones siempre tenemos un emisor (conductor) y un receptor (cliente), como en un proceso comunicativo. La diferencia es que el emisor va a estar en constante movimiento y es la ubicación de éste lo único que le interesa al receptor. Sin embargo, tenemos en el medio algún tipo de plataforma que también visualice y vele porque no ocurra ningún error en el medio; por lo tanto, tendríamos un esquema de tres entidades::
Figura 1.
Tres entidades para el seguimiento en tiempo real
Este esquema permite que el emisor emita su ubicación bajo determinadas condiciones a la entidad central que por ahora llamaremos una base de datos y esta estará disponible para ser consultada por el receptor cuando lo desee y refleja con exactitud la última posición conocida del emisor.
Listemos entonces las operaciones que necesitamos para cada una de las entidades: enviarPosicionCada(xTiempo ó x Distancia), guardarUltimaPosicion(), notificarReceptor() y actualizarMapa(), Por tanto podríamos reflejar estas consideraciones en nuestro diagrama de la siguiente manera:
Figura 2.
Operaciones de las entidades para el seguimiento en tiempo real
Tanto si queremos hacer la aplicación del domiciliario/driver o la aplicación del usuario que solicita los domicilios, es clave poder detectar las posición de ambos. También vamos a tener que programar una función que se encargue de actualizar dicha posición para el caso del domiciliario según nuestros requerimientos. Finalmente el usuario deberá ser capaz de ver en todo momento dónde está el domiciliario y la ruta que está siguiendo y para esta parte utilizaremos Google Maps en Angular. Listemos entonces estas funcionalidades:
Geolocalización del cliente y del domiciliario/conductor.
Envió cada x tiempo la posición del domiciliario a una base de datos
Cada vez que se actualice la posición del domiciliario enviarla a la aplicación del cliente para que el mapa se actualice
Como usuario permitir visualizar el mapa con la posición del domiciliario y mi posición.
Geolocalización con Javascript
Exploremos entonces como Javascript mediante la API de geolocalización del navegador nos permite detectar dónde está el usuario:
window
.
navigator
.
geolocation
.
getCurrentPosition
(
funcionQueLlamoEnCasoDeExito,
functionQueLlamoEnCasoDeFracaso)
Nota que en Google Chrome puedes acceder muy fácilmente a la ubicación de quien abre tu aplicación con esas simples instrucciones de código javascript. Nota que debes tener definidas las funciones para el caso en el que el GPS obtenga la posición o para los casos en que no obtenga la posición, la función que llamas en caso de éxito recibirá como parámetros las coordenadas que el GPS pudo encontrar, de la siguiente manera:
funcionQueLlamoEnCasoDeExito(
position
) {
console.log(
position
)
{...
coords
: {
latitude
:
6.208488
,
longitude
:
-
75.563577
}
...
}
}
Y la función que llamarás en caso de error simplemente recibirá como parámetro el error que obtuvo el GPS:
funcionQueLlamoEnCasoDeFracaso(
error
) {
// Haz algo
}
Los tipos de errores más probables son que el usuario no le dió permiso el navegador de acceder a su localización o que definitivamente el GPS no pudo encontrar dónde estamos. Esto puede ser ocasionado si estamos en un sótano desde un celular o si el computador donde estemos no tiene conexión a internet. Veamos ahora qué uso le podemos dar a dicha posición para que en ambas aplicaciones se pueda tanto al cliente como al domiciliario/conductor.
Enviando y almacenando la posición del domiciliario
Ahora que sabemos cómo localizar al usuario a través del navegador usando el acceso al GPS y la geolocalización que se nos provee, necesitamos hacer algo con dicha posición. Un primer borrador de cómo se vería dicha función sería algo similar a esto:
async
function
gpsTracking
() {
const
coords
=
await
getCurrentPosition
();
await
sendToServer
(
coords
)
// optional delay;
gpsTracking
();
}
Esta función obtendrá las coordenadas y tan pronto como las obtenga las enviará al servidor, luego esperará un poco y se volverá a ejecutar indefinidamente mientras la aplicación esté abierta. Con
Firebase
es muy simple hacer esto si tenemos instalado y configurado firebase en nuestro proyecto bastará con modificar un poco nuestra función para guardar las coordenadas en una colección de firestore o en un nuevo objeto de la base de datos on realtime de Firebase.
await
this.
driversCollection
.
doc
(driverId).
set
(
coords
)
Con esta simple línea estamos guardando en el id del conductor la posición actual de manera que Firebase almacenaría dicha posición en un objeto clave valor de la siguiente manera:
{
“Uja21-w12-jklj”:
{
latitude
:
6.208488
,
longitude
:
-
75.563577
}
}
Colocando todo esto junto, nuestra función se vería de la siguiente manera:
updateDriversPosition
(driverId) {
let
position
=
{
lat
:
4.682905
,
lng
:
-
74.070275
}
;
const
step
=
0.000006
;
//
Play with the distance of every driver step
const
refreshTime
=
400
;
//
Play with the refresh position of the driver
const
refreshId
=
setInterval
(
async
()
=>
{
position
.
lat
-=
step
;
position
.
lng
+=
step
;
await
this
.
driversCollection
.
doc
(driverId)
.
set
(position)
if
(
position
.
lat
<
4.679705
)
{
clearInterval
(refreshId)
;
}
},
refreshTime);
}
Hemos puesto 400ms en el tiempo de refresh para enviar una nueva posición pero tu puedes jugar con esto como quieras para obtener distintos comportamientos. Ahora exploremos cómo el usuario que encargó el domicilio o el conductor obtiene dicha posición.
Recibiendo la posición del domiciliario
Para obtener la posición del domiciliario nos vamos a valer de las funcionalidades que tiene Firebase ya que cada vez que se actualiza la posición del lado del conductor y sin hacer absolutamente nada más que estar suscrito a la colección del driver nos van a llegar las posiciones automáticamente y listas para ser pintadas y dibujadas. ¿Cómo es esto posible?. En la siguiente porción de código lo vemos:
async
requestTo
(destination) {
const
response
=
await
this
.
lookForDriverTo
(destination)
this
.
assignedDriverPosition$
=
this
.
driversCollection
.
doc
(
response
.
driver
.
id
)
.
valueChanges
();
...
}
Cuando se solicita un conductor se llamará esta función y luego se invocará al servidor para que encuentre el mejor conductor disponible, dicha respuesta vendrá con un objeto driver y un id que es la que utilizaremos para suscribirnos y ver los cambios de las posiciones:
...
this
.
assignedDriverPosition$
=
this
.
driversCollection
.
doc
(
response
.
driver
.
id
)
.
valueChanges
();
this
.
assignedDriverPosition$
.
subscribe
(position
=>
{
/**
Paint driver or do something with the position
*/
})
...
Así pués no queda más que aprender a pintar dicha posición en un mapa usando la API de Google Maps para Javascript.
Pintando las posiciones en el mapa
Empezar con la API de Google Maps para Javascript es muy muy fácil solo necesitamos tener una key valida que la puedes sacar de la consola de Google e importar el script en tu archivo index.html:
<
script
async defer
src
=
"
https://maps.googleapis.com/maps/api/js?key=
<TuSuperKeySeguraYSecreta"
type
=
"text/javascript"
>
</
script
>
Al hacer esto se nos incluye en el entorno de ejecución Google como objeto y podemos empezar a hacer cosas increíbles con el Google.Maps, como entonces pintamos un mapa dentro de un componente de Angular:
@
ViewChild
(
"map"
, {
read
:
false
,
static
:
true
})
mapElement
:
ElementRef
;
<
h1
>...</
h1
>
<
div
#map
id
=
"map"
></
div
>
<
otro-componente
></
otro-componente
>
Necesitamos usar ViewChild para enlazar nuestro componente con el mapa y el html mediante el id, luego de esto tendremos la propiedad
map
disponible en nuestro componente.
Para poder pintar un marcador en el mapa vamos a requerir primero crear una latitud y una longitud, luego configurar algunas opciones del mapa y finalmente enlazarlo con la referencia que tenemos a nuestro mapa con la propiedad
this
.
map.
let
latLng
=
new
google
.
maps
.
LatLng
(
6.236654
,
-
75.580432
);
let
mapOptions
=
{
center
:
latLng
,
zoom
:
13
,
mapTypeId
:
google
.
maps
.
MapTypeId
.
ROADMAP,
Styles: [...]
};
this
.
map
=
new
google
.
maps
.
Map
(
this
.
mapElement
.
nativeElement
,
mapOptions
);
Hay una propiedad especial que es la propiedad styles que nos permite personalizar los estilos del mapa para que luzcan mejor acorde a las necesidades que tengamos, aquí podrás encontrar más
estilos para tus mapas
.
Lo que nos quedaría entonces sería sobre nuestro mapa ser capaces de pintar la posición del domiciliario mediante un marcador, pero en realidad lo que va a suceder es que el marcador se borrará y pintará cada vez que llegue una posición ya que no es posible actualizar la posición del marcador de manera directa. Veamos cómo sería esto:
let
latLng
=
new
google
.
maps
.
LatLng
(
position
.
coords
.
latitude
,
position
.
coords
.
longitude
);
this
.
myMarker
=
new
google
.
maps
.
Marker
({
map
:
this
.
map
,
animation
:
google
.
maps
.
Animation
.
cn
,
position
:
latLng
,
icon
: {
url
:
"assets/photo.jpg"
}
});
Y para borrar y pintar el marcador sería algo así:
this
.
myMarker
.
setMap
(
null
);
this
.
myMarker
=
null
;
Poniendo todo junto podrás ver el siguiente repositorio con la implementación completa de un clon de Uber hecho en Angular usando la API de Google Maps para Javascript y Angular Maps.
https://github.com/seagomezar/uber-like-app-angular
Eso es todo, espero que este post te sea de utilidad y lo puedas aplicar a algún proyecto que tengas en mente y que simplemente te haya ayudado a entender la naturaleza de las aplicaciones “On Real Time” como Uber o Rappi. Déjame un comentario si lograste implementarlo, si quieres añadir alguna otra funcionalidad o si tienes alguna duda no dudes en dejarme un comentario en la parte de abajo, recuerda que si te gustó también puedes compartir usando los links a las redes sociales en la parte de abajo.
Sobre el Autor:
Sebastian Gomez
Google Developer Expert en Web Technologies
Twitter: @sebasgojs
Website:
www.sebastian-gomez.com
Labels
.app
.dev
.txt
#AMP
#CPU
#DeveloperStudentClubs
#DevFest
#DragonBall
#DSC
#Forsety
#ForsetySecurity
#freeandopen
#GCP
#Google
#GoogleCloud
#GoogleCloudPlatform
#GoogleLaunchpad
#iio2009
#Kubernetes
#MaterialDesign
#OneCommunity
#Security
#TensorFlow
#UPGlobal
#UpLatam
#WithGoogle
+page
10 YEARS
2013
2019
64 bits
A/B Testing
AA
Accelerator
Action on Goolge
actionbar
Actions
Actions Console
AdMob
Ads
adwords
adwords api
AI
AIY
ajax
alarmmanager
ALFA
almacenamiento
alojamiento de proyectos en google code
AMP
AMP Conf
AMP Project
amp-date-picker
amphtml
Analytics
Andorid
android
Android (operating System)
Android 3.1
android 3.3
android 4.2
android 9
Android 9 Pie
Android App Bundle
android design
Android Dev Summit
Android Developers
android Jetpack
Android P
Android SDK
Android Studio
Android Things
Android Wear
AndroidDevStory
androititlan
angelina jolie
Annotation
Announcements
anuncios
API
API Analytics YouTube
Apigee
APIs
Aplicaciones
aplicaciones chrome
app
app engine
App Indexing
app invites
App Server
applications
AppQuality
apps
Apps Script
AR
ARCore
arte
ATLAS
AWP
backend
Base64
batch
Bava
Betatesting
Better Ads Standars
bigdata
BigQuery
Biometrics
blink
bootcamp
BOT
BQ
Business
búsqueda ajax
by Google
byCases
byCommunity
byDevelopers
byGoogle
C++
CALENDAR
Cardboard
case
caso de éxito
Casos de éxito
casos destacados
CCOSS
Century Fox
chat
chrome
chrome web store
chromebook
chromecast
chromium
Cinéfilos
cloud
Cloud Anchors
CLOUD endpoints
Cloud Firestore
Cloud Functions
Cloud IoT Core
Cloud Next
Cloud Scheduler
Cloud services
cloud test lab
Cloud Text-to-Speech
Cloud Translation
CMD en vivo
coconut
code
code-in
code.org
CodeLabs
código
código abierto
Colab
colombia
Communities
Comunidades
concurso google
conference
contenedores
convocatoria
Coordinate
crashlytics
CRE
crear aplicaciones ajax
creatividad
Crowdsource
CSS
cws
daniela robles
dart
dart sdk
dartium
dartlang
Dataset
DCL
denis labelle
desarrolladores
Desarrolladores Google
desarrolladores LatAm
Desarrollar
Design
Design Sprint
Destacados
dev
Dev.f
DevArt
DevBus
DevBusLatAm
Developer Bus
Developer Summit
DeveloperConsole
developers
DevFest
devoxx
dialogflow
diseño UX
Distribuir
DNS
DOM
domain
DonkeyCar
doubleclick
Drive SDK
Drivers
ecommerce
ecosistema
elections
elizalde
Emoticons
emprendedores
empresas
engagement
english
Enhanced Campaigns
enterprise
eventos
Events
evolución de aplicaciones
Excel
ExpertosDicen
Faas
Family
FanBridge
FCM
FCP
Featured
fido
find people
Fintech
firebase
Firebase Cloud Messaging
firebase summit
flu trends
Flutter
Flutter 1.0
flutter 1.7
flutter developers
Flutter Live
FlutterLive
FoundersLab
Freebase
Fuction
Fuctions
Full-Stack
functional programming
G Suite Dev Show
G+
g+ goto gal
G+GotoGal
GAE
game
games
GCloud
gcm
GCP
GCS
GDA
GDE
GDG
GDH
GDL
GDLevent
GDS
Get Inspired
get.app
GitHub
GLP
gmail
golang
GOMO
Google
Google Accelerator
Google AdMob SDK
Google AdWords
Google Analytics
Google APIS
Google App Engine
Google Apps
Google Apps Script
Google Art Project
Google Assistant
google calendar
google cast
Google Charts
Google Chrome
Google Cloud
Google Cloud Console
Google Cloud Messaging
Google Cloud Next
Google Cloud Platform
Google Cloud Platform Newsletter
google cloud platforn
Google Cloud Storage
google code-in
Google Compute Engine
Google Dataset
Google Developer Groups
google developers
Google Developers Academy
google developers expert
Google Developers Hackademy
google dns
Google Drawings
Google Drive
Google Earth
Google for games
Google Forms
google geo
Google Home
google i/o
google i/o extended
google io
Google Keep
Google Kubernetes Engine
Google Launchapad
Google Launchpad
Google Maps
google maps coordinate
Google Maps Platform
Google Mexico
Google Nose
google now
Google Person Finder
google places api
Google Play
Google Play Books
Google Play Developer API
google play games
Google Play Movies
Google Play Protect
Google Play Services
Google Plus
Google Science Fair
google search
Google Sheets
google sign in
Google Top Geek
Google+
Google+ Communities
Google+ Hangouts
google+ sign-in
GoogleAPI
googlecloud storage
GoogleCloudPlatform
googledevs
GooglePlay
Googleplex
Goolge Lunchpad
GTG
Hackademy
hackers
Haiko
Haití
hangouts
Hangouts Remote Desktop
hardcode
Heello
honeycomb
HTML
HTML5
HTTPS
I/O
IA
IAM
IETF
IFAI
in app purchases
in-app
ingles
Ingress
instagram
integración de soluciones
interactive post
Interesante
International
International Women’s Day
IO
io15
io18
io19
iOS
IoT
istio
IU
IVR
J2EE
java
JavaScript
jelly bean
JS
JSON
Juegos
juegos html5
Kit ML
Knative
kotlin
kUBERNATES
Kubernetes
LATAM
latamRegionSur
Launchpad
Launchpad Studio
Lenovo Mirage Solo
lightbox
linux
lucero galindo
machine learning
Made with Code
Mapdata
Mapeo
maps
Maps Ad Unit
Maps API
Maps Engine
Market
Marketing
Marshmallow
MATERIAL DESIG
Material Design
mejores apps 2013
México
michelle marie
MIT
MIT Global Start-up Labs
MIT-AITI
ML
ML Kit
mobile
monetizar
mongoDB
MOOC
Motorola
Mountain View
móvil
MQTT
mr.white
mTLS
natalie villalobos
Navigation
NBA JAM
NES
Next Big Sound
Next Level
nfc
Niantic
Nik
NINTENDO
node.js
NoSQL
nube
OAuth2
Objective-C
OClock
open source
OPenApi
OS
OSS
Paas
PageSpeed
PagesSpeed
parallel18
patrones
patters
performance
permisos
Pipeline API
Pixability
pixel
Píxel
play
Play Console
Playtime
Podcast
pollito pio
Polymer
por lote
Posse
Prediction API
primer
Producto
programación
Propositos
Protocol Buffers
proyecto 20%
Push API
PYMES
python
Q
Q4
quickoffice
Rasberry Pi Zero WH
Raspberry Pi
Realtime
Reflectly
register
Release
Resources
robots.txt
Safe
SDK
Search
Security
seedbank
seguridad
SEO
servidores
Showyou
sign-in
SNES
SO
social media
Spain
SpLATAM
SQL
SQLite
Start
startup grind
Startup Launch
startup weekend
startup weekend for the planet
startupbus
startups
StayAtHome
story
Street View
subtitles
success
sw
SyScan
tablet
Tablet Optimization Tips
tabletas
takeaction
Tango
tendencias 2013
TensorFlow Developer Summit
testing
TextView
TF JAM
The Garage
The Venture City
tips G+
tips gmail
TLD
TLS
Top Experts
Top Geek
top level domain
TopExpert
topics
traducciones
Transparency Report
triggers
Tubular Labs
twilio
Tyka
TypeScript
UAC
udacity
ui
Umbrales
UNAM
unity
Unity3D
universal search
UX
Vector
VectorDrawable
video juegos
vidIQ
ViewPager
Visual Progress
Voicekit
VPC
VR
VSCode
web
Web hosting
Web móvil
WebAssembly
with google
Wizdeo
WizTracker
Women at Google
Women Techmakers
workmanager
WTM
XKCD
XML
Yifat Cohen
youtube
YouTube Analytics API
YouTube API
YouTube Data API
YouTube One Channel
YouTube Player API
Archive
2024
sept
2023
nov
oct
sept
ago
jun
may
abr
mar
ene
2022
dic
nov
oct
sept
ago
jul
jun
may
abr
mar
feb
ene
2021
dic
nov
oct
sept
ago
jul
jun
may
abr
mar
feb
2020
dic
nov
oct
sept
ago
jul
jun
may
abr
mar
feb
ene
2019
dic
nov
oct
sept
ago
jun
may
abr
mar
feb
ene
2018
dic
nov
oct
sept
ago
jul
jun
may
abr
mar
feb
2017
nov
sept
ago
jul
jun
may
abr
ene
2016
nov
oct
sept
ago
jul
jun
may
abr
mar
feb
ene
2015
dic
nov
oct
sept
ago
jul
jun
may
abr
mar
feb
ene
2014
dic
oct
sept
ago
jul
jun
may
abr
mar
feb
ene
2013
dic
nov
oct
ago
jul
jun
may
abr
mar
feb
ene
2012
dic
nov
oct
sept
ago
jul
2011
nov
oct
may
mar
2010
dic
nov
oct
sept
ago
jul
jun
may
abr
mar
feb
ene
2009
dic
nov
sept
ago
jul
jun
may
abr
mar
feb
ene
2008
oct
sept
ago
jul
jun
may
abr
mar
feb
ene
2007
dic
Feed
Desarrolladores
Eventos y Comunidad
Casos Destacados
Dicen los Expertos
Google Accelerator