PDM Avanzado GoogleMaps

De Manuais Informática - IES San Clemente.
Ir a la navegación Ir a la búsqueda

Introdución

Neste punto imos aprender como debuxar e controlar un Mapa de GoogleMaps.

Debido a que o control que debuxa o mapa fai uso dun MapFragment e esta clase foi introducida a partires da API 12 (Android 3.1) teremos dúas formas de utilizalo dependendo da API mínima ó que vaia dirixida a nosa aplicación.

Para poder utilizar un Google Map necesitaremos:

  • Google Play services (a aplicación debe estar instalada no dispositivo real. Isto xa se fai automaticamente a partires da versión 2.3).
  • Google Maps API key: Unha clave que nos proporciona Google e que ten estar posta no AndroidManifiest.xml. Para ser exactos imos necesitar dúas. Unha para a versión de desenrolo e outra para cando xeremos o APK.
  • Unha serie de permisos a engadir no AndroidManifiest.xml


Aclaración sobre o dispositivo virtual android (AVD)

Para poder utilizar o mapa nun AVD é necesario descargar unha imaxe que sexa Google API.

Unha vez descargada e tendo un AVD con esta imaxe, teremos que cambiar as propiedades do proxecto para indicarlle que imos utilizar dita imaxe:


No caso de utilizar a máquina de VirtualBox entregada para este curso, isto non será necesario.

Importación de Google Play Services ó noso proxecto

Debemos de engadir a librería de Google Play Services ó noso proxecto.

  • Nota: Se usades Android Studio neste enlace indica como tedes que facer.
  • Nota: Nun AVD funcionará nunha versión Android 4.2.2 ou superior cunha imaxe Google API.


Para facelo:


A maiores debemos engadir o seguinte permiso no arquivo AndroidManifiest.xml:

  • Dentro da entrada <application>:
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
    
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        ............



Para saber por código se un dispositivo ten o Google Play Service:

int resultCode =GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);

if (ConnectionResult.SUCCESS != resultCode) {

  // Neste caso ou non ten instalado o Google Play Services ou necesita actualizarse,...
}


En caso de erro podemos descubrir que pasa:

GooglePlayServicesUtil.getErrorDialog(resultCode, this, 0).show();

Xeración das API KEYS

Para poder utilizar un mapa de Google necesitamos xerar unha Key dende a consola de Google. Para ser máis concretos imos necesitar xerar dúas Keys, unha para facer procesos de debugger (cando estamos a desenrolar a aplicación en Eclipse) e outra xa definitiva para cando xeremos o APK que será entregado ou descargado polos usuarios da nosa aplicación.

Obtención da pegada dixital SHA1

Un paso necesario para obter esa Key é obter a pegada dixital.

Como comentamos antes imos ter dúas, unha mentres estamos a desenrolar a aplicación e outra para cando teñamos a aplicación rematada e lista para entregar ós usuarios.

Obtención da pegada dixital SHA1 Depuración

Graficamente
Dende consola

Esta información tamén se pode obter dende unha consola ou terminal. En Windows é mellor utilizar unha consola con permisos administrativos (executar como administrador).

Debemos situarnos coa orde cd (se non o temos no path) no cartafol onde estea instalado o JDK e dentro deste no cartafol /bin/.

Nese cartafol se atopa o executable keytool.

  • LINUX:
./keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android


  • WINDOWS:
keytool -list -v -keystore "%USERPROFILE%\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android

Obtención da pegada dixital SHA1 Produción

Esta a obteremos cando a aplicación estea rematada e xeremos o apk a entregar ós usuarios.

Esta parte está contemplada na Unidade de empaquetado e distribución.


Obtención da API KEY

Unha vez temos a pegada dixital, temos que acceder ó sitio web: https://code.google.com/apis/console/?noredirect

Nota: Solicitará entrar cunha conta de gmail. Se non a temos teremos que rexistrarnos e crear unha nova.

O proceso é o seguinte:

Nota Importante:

Lembrar que se queremos entregar a aplicación aos usuarios (xerar o APK) debemos de obter e utilizar a API KEY a partires da SHA1 de produción, como indicamos neste enlace.
O lóxico é que primeiro xeredes unha API KEY a partires do voso Eclipse (cando se está a facer probas). Como xa está xerada a API KEY o único que teredes que facer é editar quen ten acceso a utilizar esa api key premendo o botón 'Editar Aplicaciones Android Permitidas'. Nese intre teredes que poñer o SHA1 obtido do voso almacén de claves (obtención da SHA1).

Permisos necesarios e uso da nova API KEY

Para poder utilizar GoogleMap debemos modificar o arquivo AndroidManifiest.xml coas seguintes entradas:

  • Xusto antes da etiqueta </application>:
...........
    <meta-data
       android:name="com.google.android.maps.v2.API_KEY"
       android:value="API_KEY"/>
</application>
...........
Tendo que substituír en value API_KEY pola API KEY xerada no paso anterior.


  • Debemos engadir os seguintes permisos:
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


  • Os seguintes permisos non son obrigatorios pero se recomendan utilizalos:
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
O primeiro serve para permitir que se utilice o WIFI ou antenas móbiles para determinar a localización.
O segundo serve para permitir que se utilice o GPS para determinar a localización.


  • É necesario que a tarxeta gráfica soporte OPEN GL 2.0 para a renderización do mapa.
<uses-feature
        android:glEsVersion="0x00020000"
        android:required="true"/>

Renderizado do MAPA

Debemos crear un layout para a activity no que se visualizará o mapa.

Aquí temos varias opcións:

  • Podemos facer que o mapa ocupe todo o layout:
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/map"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:name="com.google.android.gms.maps.MapFragment"/>


  • Podemos facer que o mapa forme parte dun layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
>

	<fragment 
          android:id="@+id/map"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:name="com.google.android.gms.maps.MapFragment"/>

</RelativeLayout>


  • Unha vez definido o layout creamos a activity que o carga:
import android.app.Activity;
import android.os.Bundle;

public class UD6_02_Mapa extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.layout_anteriormente_definido);
	}
}


Se facemos todos os pasos indicados teremos como resultado algo parecido a isto:


Versións da API

Como podemos comprobar o mapa utiliza unha clase MapFragment para visualizarse.

Esta clase foi introducida a partires da API 12 (Android 3.1). Se necesitamos desenrolar unha aplicación para unha versión anterior teremos que modificar o layout e poñer o seguinte:

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/map"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:name="com.google.android.gms.maps.SupportMapFragment"/>
/>


  • Neste caso a activity debe ser unha subclase da clase FragmentActivity que se atopa na librería de compatibilidade v4:
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class UD6_02_Mapa  extends FragmentActivity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_ud6_02_mapa);
	}
}

Manexo do Mapa

Unha vez temos o mapa no layout podemos facer referencia a el dende a activity e modificar propiedades, facer zoom, mover a cámara...

Faremos uso da clase GoogleMap:

  • Para referenciar o mapa:
import com.google.android.gms.maps.GoogleMap;

private GoogleMap googleMap;

googleMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();


  • Se necesitamos que o GoogleMap funcione en versións anteriores á API 12 (Android 3.1) teremos que refencialo desta forma, facendo uso da librería de compatibilidade v4:
import com.google.android.gms.maps.GoogleMap;

private GoogleMap googleMap;

googleMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();


Entre os métodos que podemos empregar:

  • setMapType(TIPO_MAPA): Cambio o tipo de mapa, sendo TIPO_MAPA:
  • GoogleMap.MAP_TYPE_TERRAIN
  • GoogleMap.MAP_TYPE_NORMAL
  • GoogleMap.MAP_TYPE_SATELLITE
  • GoogleMap.MAP_TYPE_HYBRID


  • moveCamera(TIPO_MOVEMENTO): Move a cámara directamente.
  • animateCamera(TIPO_MOVEMENTO): Move a cámara cunha animación.
sendo TIPO_MOVEMENTO:
  • CameraUpdateFactory.zoomIn(): Aumenta en 1 o zoom.
  • CameraUpdateFactory.zoomOut(): Diminúe en 1 o zoom.
  • CameraUpdateFactory.zoomTo(nivel_de_zoom): Nivel de zoom entre 2 e 21.
  • CameraUpdateFactory.newLatLng(lat, long): Nova lonxitude e latitude.
  • CameraUpdateFactory.newLatLngZoom(lat, long, zoom): Nova lonxitude e latitude cun zoom determinado.
  • CameraUpdateFactory.scrollBy(scrollHorizontal, scrollVertical): Fai un movemento en scroll desprezando o mapa o número de pixeles indicados.
Nota: Para mover a cámara ou cambiar o zoom faremos uso da clase CameraUpdateFactory e os seus métodos de clase.


Exemplo de código:
LatLng pos = new LatLng(latitude, lonxitude);
googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(pos,15));

Importante: Fixarse como para manexar lonxitudes-latitudes temos que facer uso da clase LatLng.


  • getCameraPosition(): Devolve un obxecto da clase CameraPosition a posición da cámara. Dentro desta clase podemos chamar á propiedade target que nos devolve un obxecto da clase LatLng que representa a lonxitude e latitude da posición da cámara.
Exemplo de código:
CameraPosition posCam = googleMap.getCameraPosition();
LatLng posicion = posCam.target;


Tamén podemos obter o zoom, orientación e ángulo accedendo ás propiedades zoom, bearing e tilt.


Marcas no Mapa

Outra das aplicacións que pode ter o uso de GoogleMap é indicar mediante iconas, posicións concretas do mapa no que alberguemos certa información.

Isto o conseguimos coas clases Marker e MarkerOptions.

  • MarkerOptions: Crea unha marca nova no mapa.

A forma de agregala sería:

googleMap.addMarker(new MarkerOptions()
		.position(googleMap.getCameraPosition().target)
		.title("TI")
		.snippet("Ti nesta posición:"+ googleMap.getCameraPosition().target.toString())
		.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher)));
Onde:
  • position: Posición da marca.
  • title: Texto que aparece na marca.
  • icon: Icona asociada á marca.
  • sniper: Texto que aparece cando pulsamos sobre a marca no mapa.

A chamada ó método addMarker nos devolve o obxecto da clase Marker:


  • Marker: Representa a marca no mapa.
Marker marca;
marca = googleMap.addMarker(new MarkerOptions()
		.position(googleMap.getCameraPosition().target)
		.title("TI")
		.snippet("Ti nesta posición:"+ googleMap.getCameraPosition().target.toString())
		.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher)));
A través deste obxecto podemos obter:
  • getPosition(): A posición (obxecto da clase LatLng).
  • getRotation(): A súa rotación.
  • getTitle(): O seu título.
.....
  • remove(): eliminamos a marca do mapa.
  • setVisible(boolean): Indicamos se queremos que sexa visible a marca no mapa.

Liñas no Mapa

Algunhas veces pode sernos de utilizade 'unir' diferentes marcas no mapa (para indicar unha ruta por exemplo).

Para facelo debemos utilizar a clase PolygonOptions.

Esta clase dispón do método add no que se lle pasa a posición a engadir (en forma de obxecto da clase LatLng).

Cando se engade un obxecto desta clase ó mapa, este une os puntos.

Por exemplo:

PolygonOptions polOpt = new PolygonOptions();
polOpt.add(new LatLng(10,10));
polOpt.add(new LatLng(11,11));
polOpt.add(new LatLng(12,12));
polOpt.strokeColor(Color.BLUE);
				
googleMap.clear();
googleMap.addPolygon(polOpt);


  • Liña 5: Establece unha cor para a liña que vai unir os diferentes puntos.
  • Liña 7: Limpa o mapa de marcas e liñas previamente debuxadas.
  • Liña 8: Engade ó mapa o conxunto de liñas.




Caso Práctico

O obxectivo desta práctica é crear unha pantalla cun mapa de google.

Poderemos cambiar de tipo de mapa (Satélite / Terrestre), movernos ó centro do mapa e gardar a posición e ir uníndoas cun polígono.

O aspecto desta práctica é o seguinte:


  • Como funciona a práctica:
A nosa icona sempre se visualizará no centro do mapa. Esta será a posición que poderemos gardar como 'puntos de paso' e que despois se unirán formando unha ruta.
Na parte superior temos:
Botón Satélite: Cambia o aspecto a modo satélite.
Botón Terrestre: Cambia o aspecto a modo terrestre.
Botón Marcar: Garda nun array a posición da icona. Unha vez temos gardados máis de dous, crea un polígono unindo os puntos.
Botón Mover: Move a icona ó centro do mapa. Para mover o mapa temos que premer sobre o mapa e sen soltar, arrastrar.
Ó premer sobre a icona informa da posición e amosa un texto.


IMPORTANTE: Aspectos a ter en conta para que funcione este práctica

  • Necesitaremos xerar unha API KEY para o voso eclipse e proxecto como vimos neste punto: Obtención da API KEY.
  • Unha vez obtida debemos de escribila no sitio correspondente do arquivo AndroidManifiest.xml


Creamos a activity

  • Nome do proxecto: UD6_02_Mapa
  • Nome da activity: UD6_02_Mapa.java


Código do layout xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   
>

	<fragment 
          android:id="@+id/map"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:name="com.google.android.gms.maps.MapFragment"/>

	<Button
	    android:id="@+id/UD6_02_btnSatelite"
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content"
	    android:layout_alignParentLeft="true"
	    android:layout_alignParentTop="true"
	    android:text="Satelite" />

	<Button
	    android:id="@+id/UD6_02_btnTerrestre"
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content"
	    android:layout_alignParentTop="true"
	    android:layout_toRightOf="@+id/UD6_02_btnSatelite"
	    android:text="Terrestre" />

	<Button
	    android:id="@+id/UD6_02_btnMarcar"
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content"
	    android:layout_alignParentTop="true"
	    android:layout_toRightOf="@+id/UD6_02_btnTerrestre"
	    android:text="Marcar" />

	<Button
	    android:id="@+id/UD6_02_btnMover"
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content"
	    android:layout_alignParentTop="true"
	    android:layout_toRightOf="@+id/UD6_02_btnMarcar"
	    android:text="Mover" />

</RelativeLayout>


Código da clase UD6_02_Mapa
Obxectivo: Traballar cun mapa de Google Map utilizando marcas.

import java.util.ArrayList;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolygonOptions;

public class UD6_02_Mapa extends Activity {

	private GoogleMap googleMap;
	private Marker marcaActual;
	private ArrayList<LatLng>marcas;
	
	private void xestionarEventos(){
		
		Button btnSatelite = (Button)findViewById(R.id.UD6_02_btnSatelite);
		btnSatelite.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
			
				googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
				
			}
		});
		Button btnTerrestre = (Button)findViewById(R.id.UD6_02_btnTerrestre);
		btnTerrestre.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
			
				googleMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
				
			}
		});
		
		Button btnMover = (Button) findViewById(R.id.UD6_02_btnMover);
		btnMover.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub

				if (marcaActual != null)
					marcaActual.remove();

				marcaActual = googleMap.addMarker(new MarkerOptions()
						.position(googleMap.getCameraPosition().target)
						.title("TI")
						.snippet(
								"Ti nesta posición:"
										+ googleMap.getCameraPosition().target
												.toString())
						.icon(BitmapDescriptorFactory
								.fromResource(R.drawable.ic_launcher)));

			}
		});
		
		Button btnMarcar = (Button)findViewById(R.id.UD6_02_btnMarcar);
		btnMarcar.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
	
				marcas.add(googleMap.getCameraPosition().target);
				
				PolygonOptions polOpt = new PolygonOptions();
				for (LatLng lugar : marcas){
					polOpt.add(lugar);
				}
				polOpt.strokeColor(Color.BLUE);
				
				googleMap.clear();
				googleMap.addPolygon(polOpt);

				marcaActual = googleMap.addMarker(new MarkerOptions()
				.position(googleMap.getCameraPosition().target)
				.title("TI")
				.snippet(
						"Ti nesta posición:"
								+ googleMap.getCameraPosition().target
										.toString())
				.icon(BitmapDescriptorFactory
						.fromResource(R.drawable.ic_launcher)));

			}
		});

	}
	
	private void prepararMapa(){
		
		googleMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
		
		if (googleMap==null){
			Toast.makeText(this, "ERRO O OBTER O MAPA", Toast.LENGTH_LONG).show();
			finish();
		}
		
		LatLng pos = new LatLng(42.879985, -8.544855);	// Posición de Santiago de Compostela
		 
		marcaActual = googleMap.addMarker(new MarkerOptions()
			.position(pos)
			.title("TI")
			.snippet("Ti nesta posición:" + pos.toString())
			.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher))
				);
		
		googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(marcaActual.getPosition(),16));
	}
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_ud6_02__mapa);

		marcas = new ArrayList<LatLng>();
		prepararMapa();
		xestionarEventos();
	}
}
  • Liña 22: Definimos o mapa de google.
  • Liña 23: A marca que representa a nosa posición. É necesaria xa que se queremos movela temos que primeiro eliminala e despois volvela a crear.
  • Liña 24: Definimos o array que vai representar o conxunta de puntos que temos gardados.
  • Liña 35: Cambiamos o modo a Satélite ó premer o botón.
  • Liña 46: Cambiamos o modo a Terrestre ó premer o botón.
  • Liñas 58-69: Xestionamos o evento Click sobre o botón mover.
  • Liñas 58-59: Eliminamos a icona do mapa.
  • Liñas 61-69: Agregamos unha nova icona (marca) na posición actual da cámara.
  • Liñas 81-100: Xestionamos o evento Click sobre o botón marcar.
  • Liña 81: Agregamos a posición actual da cámara ó array de marcas.
  • Liñas 83-86: Creamos un PolygonOptions e agregamos todas as marcas gardadas no array.
  • Liña 87: Establecemos de cor azul a liña que une os puntos.
  • Liña 89: Limpa o mapa.
  • Liña 90: Agrega o PolygonOptions para debuxar as liñas entre os puntos.
  • Liñas 92-100: Agregamos unha nova icona (marca) na posición actual da cámara.
  • Liñas 107-126: Obtén unha referencia ó mapa e crea unha marca (icona) en Santiago de Compostela.
  • Liña 109: Obtén unha referencia ó mapa.
  • Liñas 116-123: Crea unha marca (icona) en Santiago de Compostela.
  • Liña 125: Move a cámara do mapa á posición da marca cun zoom de 15.



-- Ángel D. Fernández González e Carlos Carrión Álvarez -- (2014).