Inicio Android Mapas en Android – Google Maps Android API (3)

Mapas en Android – Google Maps Android API (3)

por sgoliver
mapas-3

[mensaje-curso]

En los dos artículos anteriores (I y II) del curso hemos visto cómo crear aplicaciones utilizando la API de Google Maps para Android y hemos descrito las acciones y eventos principales de los mapas.

En este último artículo de la serie nos vamos a centrar en la creación y gestión de marcadores, y en el dibujo de elementos gráficos, como líneas y polígonos, sobre el mapa.

De forma similar al artículo anterior, para esta ocasión vamos a partir de nuevo de la aplicación de ejemplo creada en el primer artículo de la serie sobre mapas, a la que añadiremos tres botones superiores para demostrar las nuevas funcionalidades que presentaremos en esta entrega.

Sin más preámbulos, empecemos por la creación de marcadores. Rara es la aplicación Android que hace uso de mapas sin utilizar también este tipo de elementos para resaltar determinados puntos de interés sobre el mapa. Agregar un marcador básico a un mapa resulta tan sencillo como llamar al método addMarker() pasándole la posición, en forma de objeto LatLng, y el texto a mostrar en la ventana de información del marcador. En nuestra aplicación de ejemplo añadiremos al primer botón una acción de este tipo, de forma que cuando lo pulsemos se añada automáticamente un marcador sobre España con el texto “Pais: España“.

btnMarcador = (Button)findViewById(R.id.btnMarcador);
btnMarcador.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
         insertarMarcador();
    }
});

//...

private void insertarMarcador() {
    mapa.addMarker(new MarkerOptions()
        .position(new LatLng(40.3936945, -3.701519))
        .title("Pais: España"));
}

Así de sencillo, basta con llamar al método addMarker() pasando como parámetro un nuevo objeto MarkerOptions sobre el que establecemos la posición del marcador (método position()) y el texto a incluir en la ventana de información del marcador (métodos title() para el título y snippet() para el resto del texto). Si ejecutamos la aplicación de ejemplo y pulsamos el botón “MARCADORES” aparecerá el siguiente marcador sobre el mapa:

marcador

Si pulsamos sobre el marcador, la vista se centrará en la posición del marcador y se mostrará la ventana de información con el texto indicado.

marcador-pulsado

Pero vemos que además aparecen por defecto en la esquina inferior derecha dos botones para mostrar dicha ubicación en la aplicación de Google Maps y para calcular la ruta al lugar marcado. Si no queremos que aparezcan estos botones, debemos llamar previamente al método setMapToolbarEnabled(false) sobre nuestro objeto GoogleMap. Podríamos hacer esto por ejemplo dentro del método onMapReady() tras obtener la referencia al mapa:

@Override
public void onMapReady(GoogleMap map) {
    mapa = map;

    mapa.getUiSettings().setMapToolbarEnabled(false);
}

Si no nos gusta el comportamiento por defecto al pulsar sobre un marcador, también tenemos la posibilidad de definirlo de forma personalizada asignando al mapa dicho evento como cualquiera de los comentados anteriormente. En este caso el listener se asignará al mapa mediante el método setOnMarkerClickListener() y sobrescribiremos el método onMarkerClick(). Dicho método recibe como parámetro el objeto Marker pulsado, de forma que podamos identificarlo accediendo a su información (posición, título, texto, …). Veamos un ejemplo donde mostramos un toast con el título del marcador pulsado:

mapa.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
    public boolean onMarkerClick(Marker marker) {
        Toast.makeText(
            MainActivity.this,
            "Marcador pulsado:\n" +
            marker.getTitle(),
            Toast.LENGTH_SHORT).show();

         return true;
    }
});

Con el código anterior, si pulsamos sobre el marcador de España aparecerá el siguiente mensaje informativo:

marcador-pulsado-2

Salvo este último punto, todo lo explicado sobre marcadores corresponde al comportamiento por defecto de la API, sin embargo también es posible por supuesto personalizar determinadas cosas, como por ejemplo el aspecto de los marcadores. Esto se sale un poco del alcance de este artículo, donde pretendía describir los temas más básicos, pero para quien esté interesado tan sólo decir que mediante los métodos icon() y anchor() del objeto MakerOptions que hemos visto antes es posible utilizar una imagen personalizada para mostrar como marcador en el mapa. En la documentación oficial (en inglés) podéis encontrar un ejemplo de cómo hacer esto.

Como último tema, vamos a ver cómo dibujar líneas y polígonos sobre el mapa, elementos muy comúnmente utilizados para trazar rutas o delimitar zonas del mapa. Para realizar esto vamos a actuar una vez más directamente sobre la vista de mapa, sin necesidad de añadir overlays o similares (para quienes recuerden versiones anteriores de la API), y ayudándonos de los objetos PolylineOptions y PolygonOptions respectivamente.

Para dibujar una linea lo primero que tendremos que hacer será crear un nuevo objeto PolylineOptions sobre el que añadiremos, utilizando su método add(), las coordenadas (latitud-longitud) de todos los puntos que conformen la linea. Tras esto estableceremos el grosor y color de la linea llamando a los métodos width() y color() respectivamente, y por último añadiremos la linea al mapa mediante su método addPolyline() pasándole el objeto PolylineOptions recién creado.

En nuestra aplicación de ejemplo haremos esto en el segundo botón de demostración para dibujar un rectángulo sobre España. Veamos cómo queda el código:

btnLineas = (Button)findViewById(R.id.btnLineas);
btnLineas.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mostrarLineas();
    }
});

//...

private void mostrarLineas()
{
    //Dibujo con Lineas

    PolylineOptions lineas = new PolylineOptions()
        .add(new LatLng(45.0, -12.0))
        .add(new LatLng(45.0, 5.0))
        .add(new LatLng(34.5, 5.0))
        .add(new LatLng(34.5, -12.0))
        .add(new LatLng(45.0, -12.0));

    lineas.width(8);
    lineas.color(Color.RED);

    mapa.addPolyline(lineas);
}

Ejecutando esta acción en el emulador veríamos lo siguiente:

lineas-poligonos

Pues bien, esto mismo podríamos haberlo logrado mediante el dibujo de polígonos, cuyo funcionamiento es muy similar. Para ello crearíamos un nuevo objeto PolygonOptions y añadiremos las coordenadas de sus puntos en el sentido de las agujas del reloj. En este caso no es necesario cerrar el circuito (es decir, que la primera coordenada y la última fueran iguales) ya que se hace de forma automática. Otra diferencia es que para polígonos el ancho y color de la linea los estableceríamos mediante los métodos strokeWidth() y strokeColor(). Además, el dibujo final del polígono sobre el mapa lo haríamos mediante addPolygon(). En nuestro caso de ejemplo, implementado en el tercer botón de demostración, quedaría como sigue:

btnPoligono = (Button)findViewById(R.id.btnPoligono);
btnPoligono.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mostrarPoligono();
    }
});

//...
    
private void mostrarPoligono()
{
    //Dibujo con Poligonos

    PolygonOptions rectangulo = new PolygonOptions()
        .add(new LatLng(45.0, -12.0),
             new LatLng(45.0, 5.0),
             new LatLng(34.5, 5.0),
             new LatLng(34.5, -12.0),
             new LatLng(45.0, -12.0));

    rectangulo.strokeWidth(8);
    rectangulo.strokeColor(Color.RED);

    mapa.addPolygon(rectangulo);
}

El resultado al ejecutar la acción en el emulador debería ser exactamente igual que el anterior.

Puedes consultar y/o descargar el código completo de los ejemplos desarrollados en este artículo accediendo a la pagina del curso en GitHub.

Y con esto habríamos concluido la serie de tres artículos destinados a describir el funcionamiento básico de API de Google Maps para Android. Espero haber aclarado las dudas principales a la hora de comenzar a utilizar esta funcionalidad. Tampoco descarto algún artículo adicional para comentar temas algo más avanzados sobre esta API, pero eso será más adelante.

[mensaje-curso]

También te puede interesar

4 comentarios

Mapas en Android – Google Maps Android API [Serie] | sgoliver.net 15/09/2016 - 16:24

[…] Mapas en Android – Google Maps Android API (3) [Act. Septiembre 2016] […]

Responder
José 11/10/2016 - 21:43

Buenas tardes, quisiera consultar sobre el addMarker, no logro que se visualice en el mapa. Alguna idea? Gracias desde ya.

Responder
Mario German 01/11/2016 - 17:55

Hola estoy intentando desde un mapa poder seleccionar un marker y enviarlo a una nueva actividad pasandole la información del ID, lat y long, pero al seleccionarlo me envía a la actividad con otra información, alguien sabe como debería hacerlo, agradezco todos los que me puedan ayudar y si tienen un ejemplo para orientarme seria de mucha ayuda.

Este es mi código:

public class MapaGeneral extends FragmentActivity implements GoogleMap.OnMyLocationChangeListener, GoogleMap.OnMarkerClickListener {

private GoogleMap googleMap;
private HashMap info_map = new HashMap();
private Marker myMarker;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mapa_general);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.

if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}

setUpMap();

}

private void setUpMap() {

ArrayList<HashMap> location = null;
String url = «http://www.lifes.com.co/vet_life/info_map_general.php»;
try {

JSONArray data = new JSONArray(getHttpGet(url));

location = new ArrayList<HashMap>();
HashMap map;

for (int i = 0; i < data.length(); i++) {
JSONObject tienda = data.getJSONObject(i);

map = new HashMap();
map.put(«tienId», String.valueOf(tienda.getInt(«tienId»)));
map.put(«lat», tienda.getString(«lat»));
map.put(«lng», tienda.getString(«lng»));
map.put(«tienNombre», tienda.getString(«tienNombre»));
map.put(«tienDescripcion», tienda.getString(«tienDescripcion»));
Log.e(«informacion_mapa», String.valueOf(map));
location.add(map);
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// *** Display Google Map
googleMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();

// *** Focus & Zoom
Double latitude = Double.parseDouble(location.get(0).get(«lat»));
Double longitude = Double.parseDouble(location.get(0).get(«lng»));
LatLng coordinate = new LatLng(latitude, longitude);
googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
googleMap.setMyLocationEnabled(true);

googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(coordinate, 24));
if (ActivityCompat.checkSelfPermission(getApplicationContext(),
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
return;
}
//googleMap.setOnMarkerClickListener(this);

for (int i = 0; i < location.size(); i++) {
latitude = Double.parseDouble(location.get(i).get("lat"));
longitude = Double.parseDouble(location.get(i).get("lng"));
String name = location.get(i).get("tienNombre");
String descip = location.get(i).get("tienDescripcion");
MarkerOptions markerOptions = new MarkerOptions()
.position(new LatLng(latitude, longitude))
.title(name)
.snippet(descip);
myMarker = googleMap.addMarker(markerOptions);
googleMap.setOnMyLocationChangeListener(this);
myMarker.isInfoWindowShown();
//myMarker.showInfoWindow();
//info_map.put(myMarker, i);
}
}

@Override
public boolean onMarkerClick(final Marker marker) {
googleMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
@Override
public void onInfoWindowClick(Marker marker) {

if (marker.equals(myMarker)) {
Intent iratienda = new Intent(getApplicationContext(), Informacion_Cliente.class);
LatLng lat_long = marker.getPosition();
int id = info_map.get(marker);
iratienda.putExtra("tienId", id);
iratienda.putExtra("lat", lat_long.latitude);
iratienda.putExtra("long", lat_long.longitude);
Log.e("que envia", String.valueOf(id));
startActivity(iratienda);
}
}
});
return true;
}

@Override
public void onMyLocationChange(Location loc) {
LatLng myposicion = new LatLng(loc.getLatitude(), loc.getLongitude());
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(myposicion, 14));
googleMap.setOnMyLocationChangeListener(null);
}

public static String getHttpGet(String url) {
StringBuilder str = new StringBuilder();
HttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
try {
HttpResponse response = client.execute(httpGet);
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 200) { // Download OK
HttpEntity entity = response.getEntity();
InputStream content = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(content));
String line;
while ((line = reader.readLine()) != null) {
str.append(line);
}
} else {
Log.e("Log", "Failed to download result..");
}
} catch (IOException e) {
e.printStackTrace();
}
return str.toString();
}

}

Responder
Mayron Guevara 08/07/2017 - 8:32

Hola, te comento que cuando quiero trazar una ruta, si la distancia es menor a 1 km me la traza bien, pero si es mayor me sale un error de SocketClosed.

String url = «https://maps.googleapis.com/maps/api/directions/json?origin=»+latitude+»,»+longitud+»&destination=»+comlat+»,»+comlon+»&mode=driving&key=XXXXXXXX»;

HttpHandler hh = new HttpHandler();
String jsonStr = hh.makeServiceCall(url);
data = jsonStr;

try {
JSONObject jsonRoute = new JSONObject(data);
if (jsonRoute.has(«status») && jsonRoute.getString(«status»).equals(«OK»)) {
if (jsonRoute.has(«routes»)) {
JSONArray rts = jsonRoute.getJSONArray(«routes»);
if (rts != null && rts.length() > 0 && !rts.isNull(0)) {
JSONObject r = rts.getJSONObject(0);
JSONObject m_poly = r.getJSONObject(«overview_polyline»);
String enc_points = m_poly.getString(«points»);
decodedPoints = PolyUtil.decode(enc_points);
// Llamamos al Mapa
Log.e(«PostExecute»,»Llamamos al Mapa»);
mapFragment.getMapAsync(ComoLlegarActivity.this);
}
}
}
} catch (Exception e) {
Log.e(«ComoLLegar»,»ERRORES:»+e.toString());
e.printStackTrace();
}

Este es el fragmento que llama al mapa una vez obtenido la ruta:

mMap = googleMap;
// Add a marker in Sydney and move the camera
LatLng myloc = new LatLng(latitude,longitud);
mMap.addMarker(new MarkerOptions().position(myloc).title(«Yo»));

PolylineOptions options = new PolylineOptions();
options.width(6);
options.color(Color.RED);
options.addAll(decodedPoints);
//options.add(new LatLng(latitude, longitud), new LatLng(comlat, comlon));

mMap.addPolyline(options);
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(myloc, 15));

y el error es que me corta el objeto JSON desde el servidor de google, esto no sucede con distancias cortas:

07-08 01:07:59.181 5096-6646/com.cdsoftsv.eway W/System.err: java.net.SocketException: Socket is closed
07-08 01:07:59.181 5096-6646/com.cdsoftsv.eway W/System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.checkOpen(OpenSSLSocketImpl.java:247)
07-08 01:07:59.181 5096-6646/com.cdsoftsv.eway W/System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.-wrap0(OpenSSLSocketImpl.java)
07-08 01:07:59.181 5096-6646/com.cdsoftsv.eway W/System.err: at com.android.org.conscrypt.OpenSSLSocketImpl$SSLInputStream.read(OpenSSLSocketImpl.java:690)
07-08 01:07:59.181 5096-6646/com.cdsoftsv.eway W/System.err: at com.android.okhttp.okio.Okio$2.read(Okio.java:135)
07-08 01:07:59.181 5096-6646/com.cdsoftsv.eway W/System.err: at com.android.okhttp.okio.AsyncTimeout$2.read(AsyncTimeout.java:211)
07-08 01:07:59.181 5096-6646/com.cdsoftsv.eway W/System.err: at com.android.okhttp.okio.RealBufferedSource.read(RealBufferedSource.java:50)
07-08 01:07:59.181 5096-6646/com.cdsoftsv.eway W/System.err: at com.android.okhttp.internal.http.HttpConnection$FixedLengthSource.read(HttpConnection.java:418)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at com.android.okhttp.okio.RealBufferedSource.read(RealBufferedSource.java:50)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at com.android.okhttp.okio.RealBufferedSource.exhausted(RealBufferedSource.java:60)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at com.android.okhttp.okio.InflaterSource.refill(InflaterSource.java:101)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at com.android.okhttp.okio.InflaterSource.read(InflaterSource.java:62)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at com.android.okhttp.okio.GzipSource.read(GzipSource.java:80)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at com.android.okhttp.okio.RealBufferedSource$1.read(RealBufferedSource.java:349)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at java.io.InputStream.read(InputStream.java:162)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at java.io.BufferedInputStream.fillbuf(BufferedInputStream.java:149)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at java.io.BufferedInputStream.read(BufferedInputStream.java:295)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at java.io.InputStreamReader.read(InputStreamReader.java:233)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at java.io.BufferedReader.fillBuf(BufferedReader.java:145)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at java.io.BufferedReader.readLine(BufferedReader.java:397)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at com.cdsoftsv.eway.utilerias.HttpHandler.convertStreamToString(HttpHandler.java:143)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at com.cdsoftsv.eway.utilerias.HttpHandler.makeServiceCall(HttpHandler.java:129)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at com.cdsoftsv.eway.ComoLlegarActivity$getPoly.doInBackground(ComoLlegarActivity.java:114)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at com.cdsoftsv.eway.ComoLlegarActivity$getPoly.doInBackground(ComoLlegarActivity.java:94)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at android.os.AsyncTask$2.call(AsyncTask.java:295)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:237)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
07-08 01:07:59.182 5096-6646/com.cdsoftsv.eway W/System.err: at java.lang.Thread.run(Thread.java:818)
07-08 01:07:59.209 5096-5096/com.cdsoftsv.eway E/ComoLLegar: ERRORES:org.json.JSONException: End of input at character 4358 of {
«geocoded_waypoints» : [
{
«geocoder_status» : «OK»,

Responder

Dejar un comentario

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