Inicio Android Mapas en Android (III): Overlays (Capas)

Mapas en Android (III): Overlays (Capas)

por sgoliver

[box type=»alert» border=»full»]Este artículo ha quedado obsoleto con la aparición de la segunda versión de la API de Google Maps para Android. Tienes disponible tres nuevos artículos del curso (IIIIII) donde aprender a utilizar esta nueva API.[/box]

Seguimos con nuestro Curso de Programación Android. En los dos artículos anteriores dedicados a la visualización y manipulación de mapas en nuestras aplicaciones Android (parte I, parte II) ya vimos cómo mostrar de forma básica un mapa y cómo manipularlo desde nuestro código para realizar las acciones más frecuentes, como por ejemplo centrarlo o desplazarlo a una posición determinada o establecer el nivel de zoom. Nos quedó pendiente comentar cómo podemos añadir nuestra propia información a un mapa  y cómo podemos responder a eventos de pulsación sobre el control. En este artículo nos ocuparemos de ambas cosas.

Empecemos por el principio. Para incluir información personalizada sobre un control MapView necesitaremos añadir sobre éste nuevas capas (overlays), donde dibujaremos toda la información adicional. Se puede incluir cualquier tipo de información en estas nuevas capas, por ejemplo indicaciones de ruta, marcadores, notas de texto…

El primer paso para definir una nueva capa de información será crear una clase java que derive de la clase Overlay. En esta nueva clase sobrescribiremos el método draw(), que es el lugar donde deberemos dibujar toda la información a incluir sobre el mapa en esta capa (por supuesto podemos añadir varias capas al mapa).

Por mostrar un ejemplo vamos a seguir trabajando con la misma apliación de ejemplo del artículo anterior, al que añadiremos algún marcador sobre el mapa. Para no complicar mucho el ejemplo, añadiremos tan sólo un marcador sobre unas coordenadas fijas (las mismas coordenadas sobre las que aparece centrado el mapa cuando se inicia la aplicación).

El método draw() recibe como parámetro un objeto Canvas sobre el que podemos dibujar directamente utilizando los métodos de dibujo que ya hemos utilizado en otras ocasiones (drawLine(), drawCircle(), drawText(), drawBitmap()…). Sin embargo, a todos estos métodos de dibujo hay que indicarles las coordenadas en pixels relativos a los bordes del control sobre el que se va a dibujar. Sin embargo, nosotros lo que tenemos en principio son unas coordenadas de latitud y longitud expresadas en grados. ¿Cómo podemos traducir entre unas unidades y otras para saber dónde dibujar nuestros marcadores? Pues bien, para solucionar esto Android nos proporciona la clase Projection, con la cual podremos hacer conversiones precisas entre ambos sistemas de referencia.

Veamos lo sencillo que es utilizar esta clase Projection. Partiremos de nuestros valores de latitud y longitud expresados en grados y a partir de ellos crearemos un objeto GeoPoint que los encapsule. Por otro lado, crearemos un nuevo objeto Projection mediante el métod getProjection() de la clase MapView (objeto recibido también como parámetro del método draw()). Este nuevo objeto Projection creado tendrá en cuenta la posición sobre la que está centrada el mapa en este momento y el nivel de zoom aplicado para convertir convenientemente entre latitud-longitud en grados y coordenadas x-y en pixeles. Para hacer la conversión, llamaremos al método toPixels() que devolverá el resultado sobre un objeto Point de salida.

Double latitud = 37.40*1E6;
Double longitud = -5.99*1E6;

Projection projection = mapView.getProjection();
GeoPoint geoPoint =
	new GeoPoint(latitud.intValue(), longitud.intValue());

Point centro = new Point();
projection.toPixels(geoPoint, centro);

Una vez que tenemos las coordenadas convertidas a pixeles, ya podemos dibujar sobre ellas utilizando cualquier método de dibujo. Veamos en primer lugar cómo podríamos por ejemplo añadir un círculo y una etiqueta de texto sobre las coordenadas calculadas:

//Definimos el pincel de dibujo
Paint p = new Paint();
p.setColor(Color.BLUE);

//Marca Ejemplo 1: Círculo y Texto
canvas.drawCircle(centro.x, centro.y, 5, p);
canvas.drawText("Sevilla", centro.x+10, centro.y+5, p);

Para que nadie se pierda, veamos el código completo de nuestra clase derivada de Overlay (en un alarde de creatividad la he llamado OverlayMapa) hasta el momento:

public class OverlayMapa extends Overlay {
	private Double latitud = 37.40*1E6;
	private Double longitud = -5.99*1E6;

	@Override
	public void draw(Canvas canvas, MapView mapView, boolean shadow)
	{
		Projection projection = mapView.getProjection();
		GeoPoint geoPoint =
			new GeoPoint(latitud.intValue(), longitud.intValue());

		if (shadow == false)
		{
			Point centro = new Point();
			projection.toPixels(geoPoint, centro);

			//Definimos el pincel de dibujo
			Paint p = new Paint();
			p.setColor(Color.BLUE);

			//Marca Ejemplo 1: Círculo y Texto
			canvas.drawCircle(centro.x, centro.y, 5, p);
			canvas.drawText("Sevilla", centro.x+10, centro.y+5, p);
		}
	}
}

Una vez definida nuestra capa de información personalizada debemos añadirla al mapa que se va a mostrar en la aplicación de ejemplo. En nuestro caso esto lo haremos desde el método onCreate() de la actividad principal, tras obtener la referencia al control MapView. Para ello, obtendremos la lista de capas del mapa mediante el método getOverlays(), crearemos una nueva instancia de nuestra capa personalizada OverlayMapa, y la añadiremos a la lista mediante el método add(). Finalmente llamaremos al método postInvalidate() para redibujar el mapa y todas sus capas.

//Obtenemos una referencia a los controles
mapa = (MapView)findViewById(R.id.mapa);

//...

//Añadimos la capa de marcadores
List<Overlay> capas = mapa.getOverlays();
OverlayMapa om = new OverlayMapa();
capas.add(om);
mapa.postInvalidate();

Si ejecutamos la aplicación en este momento podremos comprobar cómo se muestra un mapa centrado en nuestras coordenadas y se dibuja correctamente la información de nuestra nueva capa sobre él (lo resalto en rojo).

mapas-overlays-1

Una posible variante podría ser incluir algún tipo de marcador gráfico sobre el mapa, al estilo del propio Google Maps. Para conseguir esto, deberemos colocar la imagen del marcador en las distintas carpetas «/res/drawable» y dibujarlo sobre la capa utilizando el método drawBitmap(). Para cargar el bitmap a partir del fichero de imagen colocado en la carpeta de recursos utilizaremos la clase BitmapFactory y su método decodeResource(), al que tendremos que pasar como parámetro el ID del recurso. Veamos cómo quedaría esto en el código del método draw() de nuestra capa personalizada [para mayor claridad, más abajo podéis descargar como siempre el código fuente completo]:

//Marca Ejemplo 2: Bitmap
Bitmap bm = BitmapFactory.decodeResource(
		mapView.getResources(),
		R.drawable.marcador_google_maps);

canvas.drawBitmap(bm, centro.x - bm.getWidth(),
		centro.y - bm.getHeight(), p);

En mi caso, como imagen para el marcador he utilizado una similar al que se muestra en Google Maps. Si ejecutamos ahora la aplicación, veremos cómo hemos sustituido la marca textual anterior por el nuevo marcador gráfico:

mapas-overlays-2

Ya hemos visto lo sencillo que resulta mostrar información personalizada sobre un mapa. Tan sólo nos falta saber cómo podemos también reacionar ante pulsaciones del usuario sobre nuestro control.

Para conseguir esto, tendremos que sobrescribir también el método onTap() de nuestra capa personalizada. Este método nos proporciona directamente como parámetro las coordenadas de latitud y longitud sobre las que el usuario ha pulsado (en forma de objeto GeoPoint), con lo que podremos actuar en consecuencia desde nuestra aplicación.

Como ejemplo, nosotros nos vamos a limitar a mostrar en una notificación de tipo Toast, las coordenadas sobre las que se ha pulsado.

@Override
public boolean onTap(GeoPoint point, MapView mapView)
{
	Context contexto = mapView.getContext();
	String msg = "Lat: " + point.getLatitudeE6()/1E6 + " - " +
		"Lon: " + point.getLongitudeE6()/1E6;

	Toast toast = Toast.makeText(contexto, msg, Toast.LENGTH_SHORT);
	toast.show();

	return true;
}

El método debe devolver el valor true siempre que haya tratado la pulsación y NO sea necesario notificarla al resto de capas o al propio control MapView, y false en caso contrario.

Si ejecutamos de nuevo la aplicación de ejemplo y probamos a pulsar sobre cualquier lugar del mapa mostrado veremos como se muestran las coordenadas que se han seleccionado. Esto se podría utilizar por ejemplo para detectar si se pulsa sobre alguno de nuestros marcadores con el objetivo de mostrar información adicional.

mapas-overlays-3

Podéis descargar el código fuente completo de este artículo pulsando este enlace.

Con esto terminamos por el momento la serie de artículos dedicados a la geolocalización y la visualización de mapas en nuestras aplicaciones Android, aunque no descarto dedicar alguno más a este tema un poco más adelante. Como siempre estoy abierto a propuestas sobre el contenido del curso, podéis utilizar los comentarios del blog o mi dirección de correo electrónico para hacérmelas llegar.

Por cierto, aún estoy decidiendo los próximos temas a tratar en el curso. ¿Sugerencias?

Curso de Programación Android en PDF

Este curso también está disponible en PDF. Descubre cómo conseguirlo…

¿Te ha sido de utilidad el Curso de Programación Android? ¿Quieres colaborar de forma económica con el proyecto? Puedes contribuir con cualquier cantidad, unos céntimos, unos euros, cualquier aportación será bienvenida. Además, si tu aportación es superior a una pequeña cantidad simbólica recibirás como agradecimiento un documento con la última versión del curso disponible en formato PDF. Sea como sea, muchas gracias por colaborar!

Más información:

También te puede interesar

26 comentarios

Desarrollo en Android | sgoliver.net blog 27/06/2011 - 13:16

[…] Mapas en Android (III): Overlays (Capas) [Nuevo!] […]

Responder
alfredo 28/06/2011 - 13:43

Estas realizando un material excelente y muy ameno para los que estamos aprendiendo a usar al robocito verde.

Una idea para próximos artículos podría ser el uso y control de acelerometro, brújula digital, sensor de proximidad, etc.

Sigue así que nos ayudas mucho a los novatos ^^

Responder
Nach 28/07/2011 - 12:35

Como podría pintar varios geopoint? No soy capaz de pintar dos geopints Muchas gracias

Responder
Javier 29/07/2011 - 13:29

cuando clico en el mapa me desaparece todo (líneas, circulos, etc.), ¿cómo lo arreglo?

Responder
Julián 07/10/2011 - 12:29

Como sugerencia pues la comunicación entre actividades, que creo que no se trató.

Actualmente estoy intentando en mi aplicación guardar localizaciones en google maps. Sería como marcar una posición, que esta apareciese en el mapa y se enviase a la actividad principal o se guardara en la base de datos… sería interesante.

P.D Aunque para guardar esos «contactos» supongo que quizás sería más util ir editando un kml? no se. Esas son mis inquietudes, si te parece interesante hablar sobre eso en algún artículo por mi genial xD ya que estoy bastante atascado

Un saludo y muchas gracias por este trabajo.

Responder
Neo-Andurin 21/10/2011 - 11:30

Tengo un problema, me bajo el código fuente, lo instalo en mi android y me sale la aplicación solo que ni sale un mapa ni se sitúa, solo sale en una cuadricula.. sabrías porque es y como solucionarlo? Muchas gracias.

PD: Es de las mejores guías que he visto, gracias en nombre de todos los que estamos aprendiendo.

Responder
Yair Carreno 24/10/2011 - 17:45

Definitivamente estoy de acuerdo, de las mejores guías existentes. Gracias.

Responder
androidsupersoplo 10/11/2011 - 23:19

por favor puede ayudarme quiero crear una conexión con facebook poder comentar y poder dar like en grupos por ejemple quiero saber como se puede hacer eso muchas gracias bueno

Responder
Suwert 15/11/2011 - 19:25

¿Para añadir el marcador sobre unas coordenadas que le pasamos desde la clase principal? para no tener que definir las coordenadas en la clase OverlayMapa.
¿Como seria?

Responder
sevi 24/12/2011 - 12:16

Hola,me han ayudado mucho tus post,pero quería saber una cosa, a ver si me puedes ayudar. Tengo que hacer una aplicación que cuando pinches en un pais te reconozca de que pais se trata.
Muchas gracias

Responder
earl jr. 04/02/2012 - 0:40

Muy buena guia. Como sugerencia podrias hacer uso de la clase geocoder para buscar una direccion escrita. Ademas se podria añadir una caja de sugerencias con los primeros resultados ofrecidos por google.

Responder
atre 13/02/2012 - 12:47

Parece que la imagen del puntero de google no apunta realmente al centro como debería.
Con esta modificación creo que ya lo hace:

canvas.drawBitmap(bm, centro.x-(bm.getWidth()/2), centro.y-bm.getHeight(), new Paint());

Gracias y saludos cordiales.

Responder
Gloomy 08/03/2012 - 10:38

Los mejores Tutos Sobre android

Responder
Pipe 09/03/2012 - 14:20

@Neo-Andurin debe ser por la key que proporciona google para utilizar los mapas. Mírate el primer post sobre mapas de este tutorial.
Saludos

Responder
hector 18/03/2012 - 7:15

¿es posible obtener el nombre de la ciudad y la región a través de una coordenada?

Responder
Erick 03/05/2012 - 16:09

Hola, mi pregunta es la siguiente he bajado los ejemplos pero estoy trabajando con MAC y queria preguntar si por casualidad sabes cual es el codigo que tengo que utilizar para lo de MD5 en MAC, es que no tengo ni la menor idea de que hacer con eso

Gracias.

Responder
mac-Kenzie 05/05/2012 - 13:27

Hola Erick, para mac simplemente abres un terminal y ejecutas lo que te dice en esta página, a mi me funcionó perfectamente:

https://sites.google.com/site/androiduc3m/bloque-iii-localizacion/ejercicio-5-obtener-maps-api-key

Responder
mk 31/05/2012 - 19:18

Hola.
Estoy haciendo una aplicación que utiliza el maps y el canvas.
EL problema viene muestro el texto en el mapa y si quiero volver a mostrar otro texto no me borrar el anterior texto? He estado buscando la solucion por internet unas cuantas horas pero no encuentro la solución.
Gracias

Responder
estuardo 30/06/2012 - 17:33

Hola, que buen tutorial, estoy intentado hacer que cuando se pulse la pantalla se cree una imagen en el lugar pulsado pero no encuentro como pensé en llamar al método draw() desde el método onTap() pero no sé qué parámetros le paso al método draw(), gracias por las respuestas

Responder
bladimir 02/07/2012 - 16:50

hola, a ver si me puedes ayudar, necesito dibujar un punto en el mapa, pero este punto se tiene que cargar con el metodo onTap, como podria hacerlo???? gracias de antemano

Responder
DaviD 22/08/2012 - 18:03

Hola!, antes de nada muchas gracias por el tutorial, me está siendo de gran ayuda. Dicho esto, he descubierto que en este punto de crear superponer iconos, lo mejor es implementar la clase ItemizedOverlay.

Aquí dejo un enlace de cómo hacerlo: https://developers.google.com/maps/documentation/android/hello-mapview

Una de las cosas curiosas que se consiguen sólo de este modo es que Android le añada una sombra proyectada a nuestro item. Si, es una tontería, pero una tontería muy chula jeje XD

Responder
Jorge 07/12/2012 - 15:57

Te Felicito por el material!!! esta buenisimo!!, como sera de bueno que aca estan dando un curso privado de android ( de pago ) totalmente «robado» de tu material del curso.

Responder
Rafael 26/12/2012 - 3:18

No sé de dónde sale el boolean, pero sin él no funciona.

¿Por qué pasa eso?

Responder
admin 26/12/2012 - 9:30

Hola Rafael, ¿a qué boolean te refieres?

Responder
andro 04/04/2013 - 13:33

Hola, mi duda es la siguiente: tengo un mapa donde muestro con un icono las ubicaciones en las que me encuentro en cada momento, y tengo una base de datos SQLite donde guardo algunas de esas ubicaciones. Quiero que SÓLO las ubicaciones que hayan sido guardadas en la base de datos, aparezcan marcadas en el mapa cuando inicio la aplicación.

He leído algo sobre los métodos onSaveInstanceState() y onRestoreInstanceState(), pero no sé cómo usarlos en mi caso.

Espero que me puedas ayudar.

Responder
Mapas en Android (Google Maps Android API v2) – III | sgoliver.net blog 11/07/2013 - 19:02

[…] también este tipo de elementos para resaltar determinados puntos en el mapa. Si recordamos el artículo sobre la API v1, vimos cómo podíamos añadir marcadores añadiendo una nueva capa (overlay) al mapa y dibujando […]

Responder

Responder a androidsupersoplo

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información. Aceptar Más Información

Política de Privacidad y Cookies