Author: Terry Swiers
En Delphi podemos utilizar diferentes componentes que nos permitan visualizar mapas en nuestras aplicaciones. El más básico, es el componente TWebBrowser del que ya he hablado otras veces en el blog. Al final un la visualización de un Mapa (utilizando los diferentes servicios existentes) no es más que la visualización de una página web.
Si queréis ver entradas anteriores relacionadas con esto, aquí os dejo una lista:
- Generar ficheros KML de rutas; Tracks en Google Maps
- Visualizar un fichero KML en Delphi II
- Visualizar rutas con OpenStreetMap
- (Google Maps – API) Codificación Geográfica – I
- (Google Maps – API) Codificación Geográfica – II
- Google Maps en Delphi – I
- Google Maps en Delphi – II
Aparte de esto, si queréis más información sobre el tema, os puedo recomendar las webs de:
- The Road to Delphi de Rodrigo Ruz, donde podéis encontrar bastantes artículos sobre Google Maps, geolocalización, Google API,… relacionados con este tema.
- El blog de cadetill, donde además de algunos artículos relacionados podéis encontrar el componente “GMLib – Google Maps Library”, sin duda el mejor que podéis encontrar libre y con código fuente.
COMPONENTE TMAPVIEW
Una vez realizada esta introducción nos centraremos en el componente que Delphi trae en las nuevas versiones específico para plataformas móviles. El componente TMapView nos permite utilizar los servicios de:
Utilizando este componente nos abstraemos de la plataforma y podemos utilizar el mismo código para diferentes dispositivos.
CONFIGURACION ESPECIALES PARA ANDROID
Para utilizar este componente con Android deberemos obtener una “API KEY” de google para poder visualizar correctamente los mapas. La explicación completa podéis encontrarla aquí, aunque a continuación veremos los pasos resumidos.
Paso 1) Acceder a Google Developers Console con vuestra cuenta de Google
Paso 2) Crear un nuevo proyecto. La pantalla actual tiene una pinta similar a esta:
Paso 3) Lo siguiente que debemos hacer es crear un nuevo proyecto. En la parte superior de la pantalla tenéis un acceso para ello. Nos solicitará un nombre.
Tardará unos segundos y al finalizar el proceso ya tendremos nuestro proyecto “TestMapView” (que es el nombre que yo he escogido) creado y seleccionado en el desplegable superior.
Paso 4) Seleccionamos a la izquierda “biblioteca” y activamos la API: “Google Maps Android API”. Al entrar en este link aparece un botón de [HABILITAR].
Paso 5) Entrar en la sección de “Credenciales” y creamos una “Clave de API”.
Si el proceso ha sido correcto, aparecerá una ventana como la que se ve a continuación con la clave asignada. Esta clave será la que tendremos que asignar en nuestra aplicación para que pueda usar este servicio de Google.
Con esto el proceso está completo y ya podemos volver al IDE y comenzar nuestro proyecto.
VISUALIZAR MAPAS
Para visualizar un Mapa en el control TMapView nuestra aplicación Android, debemos modificar un par de cosas en la configuración de proyecto.
“Entitlement List”: Que viene a significar algo así como “Lista de derechos”. Ahí activaremos permiso para la opción “Maps Service”.
“Version Info”: en esa sección añadiremos como apikey la que nos ha proporcionado el servicio de google.
Con esta configuración, si ejecutamos la aplicación, veremos que ya nos aparece un mapa con agua (algo es algo ;-D).
Si queremos ver algo con algo más de interés (que un montón de agua), basta añadir este código al OnShow del formulario
procedure TFormMain.FormShow(Sender: TObject);
var
mapCenter:TMapCoordinate;
begin
// Centrar el mapa
mapCenter := TMapCoordinate.Create(LAT_SAGRADA_FAMILIA, LON_SAGRADA_FAMILIA);
MapView1.Location := mapCenter;
MapView1.Zoom := 14;
end;
Con estas líneas, creamos un coordenada (latitud + longitud) y la utilizamos para centrar el mapa en ella y de paso asignamos un zoom.
OPERACIONES BÁSICAS
El propio componente encapsula algunas de las operaciones básicas que podemos realizar sobre el mapa. Por ejemplo, para cambiar el tipo de mapa a visualizar, basta con utilizar la propiedad MapType con alguno de los valores permitidos.
- Normal = 1
- Satellite = 2
- Hybrid = 3
- Terrain = 4
Otra propiedad que implementa es LayerOptions, que nos permite añadir “capas” con nuevos elementos al Mapa que estemos visualizando en el memento. Así que podemos añadir 1 o varias de estas capas:
- PointsOfInterest: Para visualizar puntos de interés en el mapa.
- Buildings: Para visualizar los edificios.
- UserLocation: Para visualizar en control que nos permite acceder a la ubicación actual.
- Traffic: Nos permite ver información del tráfico actual.
Tenemos también modificar la inclinación del mapa utilizando la propiedad Tilt. Para ello debemos revisar la documentación, ya que dependiendo de la plataforma, el rango de valores es diferente y el comportamiento se ve afectado por otras propiedades como el Zoom.
Y por supuesto, el Zoom del que ya hemos hablado anteriormente.
AÑADIR MARCAS, POLILÍNEAS, POLIGONOS,…
Además de lo básico que ya hemos visto, podemos añadir objetos al Mapa.
El más básico de los que podemos tratar son la Marcas (Clase TMapMarker). Nos permitirán señalar un punto (coordenada) en el mapa. Para crear una marca, nos bastará el código siguiente:
var
MarcaMapa:TMapMarker;
descMarca:TMapMarkerDescriptor;
posicion:TMapCoordinate;
begin
// Posicion para la marca
posicion := TMapCoordinate.Create(LAT_SAGRADA_FAMILIA, LON_SAGRADA_FAMILIA);
// Crear el objeto Marker
descMarca := TMapMarkerDescriptor.Create(posicion);
// Asignar la imagen (de un TImageList, en mi caso)
descMarca.Icon := GetBitmap('imgMarca');
// Definimos si se puede arrastrar
descMarca.Draggable := False;
descMarca.Opacity := 0.8;
// Título y subtítulo para la ventana
descMarca.Title := 'Sagrada familia';
descMarca.Snippet := 'Antonio Gaudí 1882';
// Añadirla al mapa
MarcaMapa := MapView1.AddMarker(descMarca);
// Centrar el mapa en ese punto.
MapView1.Location := posicion;
end;
Con este código en el mapa veremos algo similar a esto.
Los Polígonos (TMapPolygon) y Polilíneas (TMapPolyline) son bastante similares a la forma de tratar con ellos. Básicamente tenemos una lista de puntos que hay que mostrar en el mapa. La propiedad para añadirlos es diferente, pero por lo demás son bastante similares.
Para crear una polilínea podemos utilizar este código:
NOTA: Para poder crear polilíneas y polígonos en la aplicación voy a utilizar una lista de puntos que provienen de un Track GPS, grabado directamente con el teléfono. Son coordenadas GPS que para los test están almacenados en una lista, pero que perfectamente podrían venir dados por el sensor de ubicación. |
var
numPuntos:integer;
dLat, dLon:Double;
i:integer;
center:TMapCoordinate;
...
// polylinea
desCPoly: TMapPolylineDescriptor;
mapPoly:TMapPolyline;
puntosPoly: TArray<TMapCoordinate>;
...
begin
// Crear el array de puntyos
puntosPoly := TArray.Create();
SetLength(puntosPoly, mmPuntosTest.Lines.Count);
// Añadir los puntos
for i := 0 to (mmPuntosTest.Lines.Count - 1) do begin
// Coordenadas
dLat := StrToFloatDef(AnsiReplaceText(mmPuntosTest.Lines.Names[i], '.', ','), 0);
dLon := StrToFloatDef(AnsiReplaceText(mmPuntosTest.Lines.ValueFromIndex[i], '.', ','), 0);
// Añadir el punto
if (dLat <> 0) and (dLon <> 0) then begin
puntosPoly[i].Latitude := dLat;
puntosPoly[i].Longitude := dLon;
end;
end;
// polylineas (utilizamos los puntos de test
descPoly := TMapPolylineDescriptor.Create(puntosPoly);
// Caracteristicas
descPoly.StrokeColor := TAlphaColorRec.Red;
descPoly.StrokeWidth := 6;
descPoly.Geodesic := True;
descPoly.ZIndex := 0.5;
// Añadirla al mapa
mapPoly := MapView1.AddPolyline(descPoly);
// centrar el mapa (calculado aprox. a partir de los puntos)
MapView1.Location := CenterPolyline(puntosPoly);
// Crear marcas de inicio y final
CrearMarca(MapView1, puntosPoly[0].Latitude, puntosPoly[0].Longitude, 'imgBandera');
CrearMarca(MapView1, puntosPoly[i-1].Latitude, puntosPoly[i-1].Longitude, 'imgFinish');
En este ejemplo, vemos que se añaden los puntos a un array (puntosPoly) desde un componente mmPuntosTest (TMemo). Posteriormente se crea el elemento descPoly de tipo TMapPolylineDescriptor, que es el que define las características visuales de la polilinea. Por último se añade la polilinea al mapa para visualizarla.
En este caso para completar la visualización, se añaden 2 marcas (TMapMarker), una al punto inicial y otra al punto final de la polilinea.
A continuación se muestra una imagen cómo se vería en el mapa:
En el caso de un polígono, vemos el siguiente código, muy similar. Primero definimos los puntos que delimitan el rectángulo (en este caso).
// Definimos un tamaño 5 (rectángulo INICIAL=FINAL)
SetLength(outLinePolygon.Points, 5);
// Cuatro puntos del polígono
arrPoints[0].Latitude := pLat - DELTA;
arrPoints[0].Longitude := pLon - DELTA;
arrPoints[1].Latitude := pLat + DELTA;
arrPoints[1].Longitude := pLon - DELTA;
arrPoints[2].Latitude := pLat + DELTA;
arrPoints[2].Longitude := pLon + DELTA;
arrPoints[3].Latitude := pLat - DELTA;
arrPoints[3].Longitude := pLon + DELTA;
// Punto final igual al inicial
arrPoints[4].Latitude := arrPoints[0].Latitude;
arrPoints[4].Longitude := arrPoints[0].Longitude;
A continuación lo creamos y lo añadimos al mapa:
var
numPuntos:integer;
dLat, dLon:Double;
i:integer;
center:TMapCoordinate;
begin
// genera 4 punto alrededor de uno dado
...
// polygon (utilizamos los puntos de test
descPolygon := TMapPolygonDescriptor.Create(outLinePolygon.Points);
// Caracteristicas
descPolygon.StrokeColor := TAlphaColorRec.Red;
descPolygon.StrokeWidth := 6;
descPolygon.Geodesic := True;
descPolygon.ZIndex := 0.5;
descPolygon.FillColor := TAlphaColorRec.Yellow;
// Añadirla al mapa
mapPolygon := MapView1.AddPolygon(descPolygon);
// centrar el mapa (calculado aprox. a partir de los puntos)
MapView1.Location := CenterPolyline(outLinePolygon.Points);
El resultado es el que se ve a continuación:
MOVIMIENTO Y ANIMACIÓN
En el ejemplo (y en el vídeo del final de la entrada) se muestra también algo de animación. Por ejemplo utilizando los métodos SetVisible que poseen todos los objetos. En el caso de las marcas, en el ejemplo se utiliza el método Remove para eliminar la marca creada y volver a crear en una nueva posición y de esta forma darle movimiento.
En el caso de los polígonos y polilíneas el método Remove no está disponible (o mejor dicho, ahora tiene un error), así que se ha solventado utilizando un método alternativo (menos eficiente).
Finalmente os dejo un vídeo de la aplicación funcionando, donde repaso un poco por encima las características de las que hemos hablado y cómo funcionan en un dispositivo móvil (Android).
{“video”:”https://www.youtube.com/watch?v=coK5k0qSB4s”,”width”:”600″,”height”:”335″}
UN PAR DE PROBLEMILAS…
Hay un par de pequeños bugs que afectan a TMapView y que me he encontrado durante el desarrollo de esta entrada y del ejemplo.
Los adjunto para que si veis el código sepáis porqué se ha hecho así.
Si alguno estáis interesados en este tema y os afectan podéis votarlos dentro de Quality Central para darles “prioridad”.
TMapView always on top of other controls
https://quality.embarcadero.com/browse/RSP-10707
El primero (no es grave) afecta al componente TMapView y al menú de la aplicación. Veréis que al mostrar el menú, se oculta el TMapView y al ocultar el menú de visualiza. No afecta a la funcionalidad, pero queda un poco raro. Es debido a que el componente TMapView queda “por encima” del TMultiview y lo tapa.
Problema al borrar objetos del TMapView
https://quality.embarcadero.com/browse/RSP-10364
El segundo (un poco más importante) afecta a cómo se obtienen los objetos de un Mapa, por ejemplo para borrarlos. En el caso de TMapPolyline y TMapPoligon, al ejecutar el método Remove, se obtiene una excepción (Invalid TypeCast). De ahí que la animación de la polilínea del ejemplo, realmente se haga ocultando una y creando nuevas, cuando sólo se debería utilizar un sólo objeto, para preservar la memoria.
Os adjunto el código fuente del programa
<CODIGO FUENTE DEL PROYECTO>
Y hasta aquí todo lo que hemos visto. Espero que haya sido útil.
Como siempre cualquier comentario, sugerencia, aclaración,… será bienvenida.
Hasta la próxima.
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition