
Obtener la geolocalización del cliente para usarla en nuestra aplicación Symfony2 requiere un poco de preparación previa. Básicamente, tenemos que obtener la longitud y latitud desde el navegador del cliente, que éste las envíe vía ajax a un controlador de nuestra aplicación y que dicho controlador use esta información como crea oportuno.
Como ejemplo, una aplicación que hace uso de este mecanismo para mostrar un mapa de Google Maps centrado en la posición del usuario. Para la creación del mapa usaremos el bundle IvoryGoogleMapBundle. Podemos encontrar el código completo en este repositorio de GitHub y una versión live en este enlace.
Veamos los pasos de una manera más detallada.
En primer lugar accedemos a la url asignada a un controlador que carge el ćodigo en javascript en el navegador. Dicho código llamará al método navigator.geolocation.getCurrentPosition(callback)
donde el callback recibirá como argumento de entrada un objeto que contiene los miembros coords.latitude
y coords.longitude
. Una vez que obtenemos las coordenadas las enviamos al servidor mediante una solicitud ajax.
La fucionalidad anterior queda encapsulada en el siguiente módulo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
Geolocator = (function ($) { function Geolocator() { this.getGeolocation = function getGeolocation(cb) { var latitude, longitude; if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( function (position) { latitude = position.coords.latitude; longitude = position.coords.longitude; cb({ error: false, latitude: latitude, longitude: longitude }); }, function (error) { error.error = true; cb(error); }); } }; this.sendGeolocation = function sendGeolocation(url, cb) { this.getGeolocation(function (response) { if (!response.error) { $.post(url, {latitude: response.latitude, longitude: response.longitude}, function (returnedData) { cb(returnedData); }); } else { cb(false); } }); }; } return Geolocator; })($); |
Y es invocada desde el siguiente código:
1 2 3 4 5 6 7 8 9 10 |
var geolocator = new Geolocator(); var url = document.URL.replace(/\/$/, ''); geolocator.sendGeolocation(url, function (response) { if (response.success) { window.location.replace(url + '/main'); } else if (!response) { alert('User geolocation not available'); } }); |
El siguiente controlador, de la aplicación de ejemplo para este artículo, especifica a setUserGeolocationAction
como el método destino de la solicitud ajax. igualmente, para este ejemplo, hemos optado por almacenar las coordenadas en variables de sesión y comenzar la ejecución, una vez obtenidas las coordenadas, a partir del método mainAction
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
<?php namespace AppBundle\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\Session; use Ivory\GoogleMap\Map; use Ivory\GoogleMap\Helper\MapHelper; use Ivory\GoogleMap\Overlays\Animation; use Ivory\GoogleMap\Overlays\Marker; class MainController extends Controller { /** * @Route("/", name="getHTML5Geolocation") * @Method("GET") */ public function getHTML5GeolocationAction() { return $this->render('AppBundle:Main:getHTML5Geolocation.html.twig'); } /** * @Route("/", name="setUserGeolocation") * @Method("POST") */ public function setUserGeolocationAction(Request $request) { $latitude = $request->request->get('latitude'); $longitude = $request->request->get('longitude'); if (!$this->get('session')->isStarted()) { $session = new Session(); $session->start(); } $this->get('session')->set('latitude', $latitude); $this->get('session')->set('longitude', $longitude); $return = json_encode(array("success" => true)); return new Response($return, 200, array('Content-Type' => 'application/json')); } /** * @Route("/main", name="main") */ public function mainAction() { //If there's no session we need to retrieve user geolocation if (!$this->get('session')->has('latitude')) { return $this->redirect($this->generateUrl('getHTML5Geolocation')); } $map = $this->createMap(); return $this->render('AppBundle:Main:main.html.twig', array('map' => $map)); } private function createMap() { $latitude = $this->get('session')->get('latitude'); $longitude = $this->get('session')->get('longitude'); $map = new Map(); $map->setPrefixJavascriptVariable('map_'); $map->setHtmlContainerId('map-canvas'); $map->setAsync(false); $map->setAutoZoom(false); $map->setCenter($latitude, $longitude, true); $map->setMapOptions( array( 'disableDefaultUI' => true, 'disableDoubleClickZoom' => true, 'zoom' => 18, 'mapTypeId' => 'roadmap' ) ); $map->setLanguage('es'); $marker = new Marker(); $marker->setPrefixJavascriptVariable('marker_'); $marker->setPosition($latitude, $longitude, true); $marker->setAnimation(Animation::DROP); $marker->setOptions( array( 'clickable' => false, 'flat' => true, ) ); $map->addMarker($marker); return $map; } } |
Por último comentar que existe un caso límite en el que el código ejecutado en el navegador no puede obtener la geolocalización del usuario, ya sea porque el usuario se ha negado a aceptar compartirla o porque el servicio de Google no puede obtenerla. En este caso tenemos la opción de comunicarnos con una acción de nuestro controlador que se encargue de efectuar la geolocalización desde el servidor, de una manera menos exacta basada en IP Geolocation, como pueda ser la ofrecida por el siguiente Bundle.