Mapas en Android

Por 9.99€ al mes tendrás acceso completo a todos los cursos. Sin matrícula ni permanencia.

Recomendación

No probar ejercicios que impliquen geolocalización en el emulador. Unos funcionan, otros no, otros funcionan mal, etc.

Geolocalización

La podemos obtener por 3 medios:

  • GPS: (el más preciso y lento (puede tardar incluso más de un minuto)). 2 metros de error.
  • Red móvil: El móvil detecta 3 antenas, hace un triángulo (el método se llama triangulación) y detecta la posición (con un margen de error de 20, 50 m…) depende de la distancia con las antenas. En ciudad hay menos error que en el campo. Tarda unos segundos
  • Wi-Fi: Funciona si estoy conectado a una red WI-FI. El móvil detecta el SSID (nombre de la red) de la wi-fi y accede a una base de datos donde tiene registrada su ubicación.

La geolocalizacion del emulador no funciona correctamente pq no tiene ni gps, ni red móvil ni han integrado la geolocalización por wi-fi.
Para falsear la geolocalización del emulador: window-> show view -> other -> emulator control
Habrá que asegurarse de tener el device seleccionado en el momento de pulsar el botón de send.

Habrá que añadir los siguientes permisos:
<!--Localización usando la red WI-FI: -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--Localización usando el GPS -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Location Manager

Vamos a mostrar las coordenadas en un TextView

Tendremos que solicitar permisos al usuario de forma similar a como lo hicimos cuando queríamos acceder a los contactos.

private static final int MY_PERMISSIONS_CODE = 999;

private TextView coordenadas;

private LocationManager locationManager;
private LocationListener listener = new LocationListener() {
 public void onLocationChanged(Location location) {
  coordenadas.setText("Latitud: " + location.getLatitude() + "Longitud: " + location.getLongitude());
 }

 @Override public void onStatusChanged(String provider, int status, Bundle extras) {}
 @Override public void onProviderEnabled(String provider) {}
 @Override public void onProviderDisabled(String provider) {}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.mapas_locationmanager);
 coordenadas = findViewById(R.id.coordenadas);
 locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
 checkPermisions();
}

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
 switch (requestCode) {
  case MY_PERMISSIONS_CODE: {
   if (grantResults.length>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED) {
    requestLocation();
   } else {
    // permission denied, boo! Disable the  functionality that depends on this permission.
   }
  }
 }
}

public void onResume() {
 super.onResume();
 requestLocation();
}

public void onPause() {
 super.onPause();
 locationManager.removeUpdates(listener);
}

private void checkPermisions() {
 if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
    ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
   ActivityCompat.requestPermissions(this,
     new String[]{
       Manifest.permission.ACCESS_FINE_LOCATION,
       Manifest.permission.ACCESS_COARSE_LOCATION,
       Manifest.permission.INTERNET,
     },
     MY_PERMISSIONS_CODE);
  } else {
   requestLocation();
  }
 } else {
  requestLocation();
 }
}

@SuppressLint("MissingPermission")
private void requestLocation() {
 locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 500, 0, listener);
 locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 500, 0, listener);
}

Añadir un mapa

¿Qué es un fragmento?

Es una parte de una Activity, que tiene su propio ciclo de vida, recibe sus propios eventos de entrada, y que se puede añadir o quitar dinámicamente.

Paso 1: Cargar las librerías

  1. Obtener una Map API Key (modo debug)https://cloud.google.com/console → Seleccionamos en la barra horizontal superior el proyecto actual y se abrirá un panel que nos permitirá crear un nuevo proyecto → Create Project → APIs → go to the api’s overview → enable API → Maps SDK for Android → Enable → Credentials → Credenciales en APIs y servicios → Create Credentials → Clave de API → anotamos la API key.
  2. Vista Android → Gradle Scripts → build.gradle (de módulo) → en dependencias añado lo siguiente:
implementation 'com.google.android.gms:play-services-auth:17.0.0'
implementation 'com.google.android.gms:play-services-base:17.1.0'
implementation 'com.google.android.gms:play-services-maps:17.0.0'

3. Además, en el layout habrá que añadir el siguiente código que representará el mapa:

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.google.android.gms.maps.SupportMapFragment"/>

Paso 2: modificar el AndroidManifest.xml

 <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="AIzaSyDKH-mnui5hMmO3wnTdrmXy4V5IVs6KTR8" />
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
    </application>
</manifest>

Paso 3: Añadir el mapa en el código Java

public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);

 SupportMapFragment mapFragment  = (SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map);
 mapFragment.getMapAsync(this);
}

Centrar mapa en la posición del usuario y seguirlo

@SuppressLint("MissingPermission")
private void requestLocation() {
 Location currentLocation = null;
 for(String provider: locationManager.getAllProviders()){
  Location aux = locationManager.getLastKnownLocation(provider);
  if(aux!=null){
   Log.d("traza", "best Provider " + provider);
   currentLocation = aux;
   break;
  }
 }
 if (currentLocation!=null) {
  updateMapaWith(new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude()));
 }
 Log.d("traza", "last location " + currentLocation);
 locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 500, 0, listener);
 locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 500, 0, listener);
}

private void updateMapaWith(LatLng latLng){
 if (googleMap != null) {
  CameraPosition camPos = new CameraPosition.Builder()
  .target(latLng)   //Centramos el mapa en Madrid
  .zoom(19)          //Establecemos el zoom en 19
  .bearing(45)       //Establecemos la orientación con el noreste arriba
  .tilt(70)          //Bajamos el punto de vista de la cámara 70 grados
  .build();

  CameraUpdate camUpd = CameraUpdateFactory.newCameraPosition(camPos);
  googleMap.animateCamera(camUpd);
 }
}

Añadir un marcador

googleMap.addMarker(new MarkerOptions()
 .position(new LatLng(41.431725, 2.192188))
 .snippet("Tadel Formació")
 .title("Centre de formació")
 .icon(BitmapDescriptorFactory.fromResource(R.drawable.escudo)));

Ejercicio Sqlite

Hacer una aplicación que utilizando una base de datos SqlLite situe en pantalla 3 puntos, identificados por el logo suministrado. Al pulsar sobre el logo, iremos a una pantalla dónde se mostrará información del enclave pulsado.

Recursos:

Utilizaremos el HashMap tabla para que al hacer click en un marker podamos obtener su id y a continuación pasársela al siguiente activity:monumentos es un HashMap de objetos Monumento.

@Override
public boolean onMarkerClick(Marker arg0) {
 int idMonumento = tabla.get(arg0);   
 startActivity(new Intent(this,LugarActivity.class).putExtra(MONUMENTO, monumentos.get(idMonumento)));
 return true;
}
Para recibir el monumento:
Monumento monumento = (Monumento) getIntent().getExtras().getSerializable(GeolocalizacionCapasActivity.MONUMENTO);

Obtener la ubicación de cierto lugar

String direccion = "Camino de Rubin 2, Gijón";

Geocoder geocoder = new Geocoder(this);  
List<Address> addresses;
addresses = geocoder.getFromLocationName(direccion, 1);
if(addresses.size() > 0) {
 double latitude= addresses.get(0).getLatitude();
 double longitude= addresses.get(0).getLongitude();
 tv.setText("Latitud="+latitude+" Longitud="+longitude);
}

Rutas

Debemos activar la Directions API en la sección API’s overview de la Google Developer Console.

También debemos activar la facturación para esta cuenta. Ojo con todo lo que ello conlleva.

MainActivity.java
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback, TaskLoadedCallback {

    private GoogleMap mMap;
    private MarkerOptions place1, place2;
    Button getDirection;
    private Polyline currentPolyline;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rutas);
        //27.658143,85.3199503
        //27.667491,85.3208583
        place1 = new MarkerOptions().position(new LatLng(22.3039, 70.8022)).title("Location 1");
        place2 = new MarkerOptions().position(new LatLng(23.0225, 72.5714)).title("Location 2");

        new FetchURL(MainActivity.this).execute(getUrl(place1.getPosition(), place2.getPosition(), "driving"), "driving");

        SupportMapFragment miMapa = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);

        miMapa.getMapAsync(this);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
        Log.d("mylog", "Added Markers");
        mMap.addMarker(place1);
        mMap.addMarker(place2);

        CameraPosition googlePlex = CameraPosition.builder()
                .target(new LatLng(22.7739,71.6673))
                .zoom(7)
                .bearing(0)
                .tilt(45)
                .build();

        mMap.animateCamera(CameraUpdateFactory.newCameraPosition(googlePlex), 5000, null);
    }

    private String getUrl(LatLng origin, LatLng dest, String directionMode) {
        // Origin of route
        String str_origin = "origin=" + origin.latitude + "," + origin.longitude;
        // Destination of route
        String str_dest = "destination=" + dest.latitude + "," + dest.longitude;
        // Mode
        String mode = "mode=" + directionMode;
        // Building the parameters to the web service
        String parameters = str_origin + "&" + str_dest + "&" + mode;
        // Output format
        String output = "json";
        // Building the url to the web service
        String url = "https://maps.googleapis.com/maps/api/directions/" + output + "?" + parameters + "&key=AIzaSyBKJ43pjK3x1GQjnO0zaYUwDJHdy5LiooU";
        return url;
    }

    @Override
    public void onTaskDone(Object... values) {
        if (currentPolyline != null)
            currentPolyline.remove();
        currentPolyline = mMap.addPolyline((PolylineOptions) values[0]);
    }
}
TaskLoadedCallback.java
public interface TaskLoadedCallback {
    void onTaskDone(Object... values);
}
FetchURL.java
public class FetchURL extends AsyncTask<String, Void, String> {
    Context mContext;
    String directionMode = "driving";

    public FetchURL(Context mContext) {
        this.mContext = mContext;
    }

    @Override
    protected String doInBackground(String... strings) {
        // For storing data from web service
        String data = "";
        directionMode = strings[1];
        try {
            // Fetching the data from web service
            data = downloadUrl(strings[0]);
            Log.d("mylog", "Background task data " + data.toString());
        } catch (Exception e) {
            Log.d("Background Task", e.toString());
        }
        return data;
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
        PointsParser parserTask = new PointsParser(mContext, directionMode);
        // Invokes the thread for parsing the JSON data
        parserTask.execute(s);
    }

    private String downloadUrl(String strUrl) throws IOException {
        String data = "";
        InputStream iStream = null;
        HttpURLConnection urlConnection = null;
        try {
            URL url = new URL(strUrl);
            // Creating an http connection to communicate with url
            urlConnection = (HttpURLConnection) url.openConnection();
            // Connecting to url
            urlConnection.connect();
            // Reading data from url
            iStream = urlConnection.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(iStream));
            StringBuffer sb = new StringBuffer();
            String line = "";
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            data = sb.toString();
            Log.d("mylog", "Downloaded URL: " + data.toString());
            br.close();
        } catch (Exception e) {
            Log.d("mylog", "Exception downloading URL: " + e.toString());
        } finally {
            iStream.close();
            urlConnection.disconnect();
        }
        return data;
    }
}
DataParser.java
public class DataParser {
    public List<List<HashMap<String, String>>> parse(JSONObject jObject) {

        List<List<HashMap<String, String>>> routes = new ArrayList<>();
        JSONArray jRoutes;
        JSONArray jLegs;
        JSONArray jSteps;
        try {
            jRoutes = jObject.getJSONArray("routes");
            /** Traversing all routes */
            for (int i = 0; i < jRoutes.length(); i++) {
                jLegs = ((JSONObject) jRoutes.get(i)).getJSONArray("legs");
                List path = new ArrayList<>();
                /** Traversing all legs */
                for (int j = 0; j < jLegs.length(); j++) {
                    jSteps = ((JSONObject) jLegs.get(j)).getJSONArray("steps");

                    /** Traversing all steps */
                    for (int k = 0; k < jSteps.length(); k++) {
                        String polyline = "";
                        polyline = (String) ((JSONObject) ((JSONObject) jSteps.get(k)).get("polyline")).get("points");
                        List<LatLng> list = decodePoly(polyline);

                        /** Traversing all points */
                        for (int l = 0; l < list.size(); l++) {
                            HashMap<String, String> hm = new HashMap<>();
                            hm.put("lat", Double.toString((list.get(l)).latitude));
                            hm.put("lng", Double.toString((list.get(l)).longitude));
                            path.add(hm);
                        }
                    }
                    routes.add(path);
                }
            }

        } catch (JSONException e) {
            e.printStackTrace();
        } catch (Exception e) {
        }
        return routes;
    }


    /**
     * Method to decode polyline points
     * Courtesy : https://jeffreysambells.com/2010/05/27/decoding-polylines-from-google-maps-direction-api-with-java
     */
    private List<LatLng> decodePoly(String encoded) {

        List<LatLng> poly = new ArrayList<>();
        int index = 0, len = encoded.length();
        int lat = 0, lng = 0;

        while (index < len) {
            int b, shift = 0, result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lat += dlat;

            shift = 0;
            result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lng += dlng;

            LatLng p = new LatLng((((double) lat / 1E5)),
                    (((double) lng / 1E5)));
            poly.add(p);
        }

        return poly;
    }
}
PointsParser.java
package com.pablomonteserin.mapas.rutas2;

import android.content.Context;
import android.graphics.Color;
import android.os.AsyncTask;
import android.util.Log;

import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.PolylineOptions;

import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;



public class PointsParser extends AsyncTask<String, Integer, List<List<HashMap<String, String>>>> {
    TaskLoadedCallback taskCallback;
    String directionMode = "driving";

    public PointsParser(Context mContext, String directionMode) {
        this.taskCallback = (TaskLoadedCallback) mContext;
        this.directionMode = directionMode;
    }

    // Parsing the data in non-ui thread
    @Override
    protected List<List<HashMap<String, String>>> doInBackground(String... jsonData) {

        JSONObject jObject;
        List<List<HashMap<String, String>>> routes = null;

        try {
            jObject = new JSONObject(jsonData[0]);
            Log.d("mylog", jsonData[0].toString());
            DataParser parser = new DataParser();
            Log.d("mylog", parser.toString());

            // Starts parsing data
            routes = parser.parse(jObject);
            Log.d("mylog", "Executing routes");
            Log.d("mylog", routes.toString());

        } catch (Exception e) {
            Log.d("mylog", e.toString());
            e.printStackTrace();
        }
        return routes;
    }

    // Executes in UI thread, after the parsing process
    @Override
    protected void onPostExecute(List<List<HashMap<String, String>>> result) {
        ArrayList<LatLng> points;
        PolylineOptions lineOptions = null;
        // Traversing through all the routes
        for (int i = 0; i < result.size(); i++) {
            points = new ArrayList<>();
            lineOptions = new PolylineOptions();
            // Fetching i-th route
            List<HashMap<String, String>> path = result.get(i);
            // Fetching all the points in i-th route
            for (int j = 0; j < path.size(); j++) {
                HashMap<String, String> point = path.get(j);
                double lat = Double.parseDouble(point.get("lat"));
                double lng = Double.parseDouble(point.get("lng"));
                LatLng position = new LatLng(lat, lng);
                points.add(position);
            }
            // Adding all the points in the route to LineOptions
            lineOptions.addAll(points);
            if (directionMode.equalsIgnoreCase("walking")) {
                lineOptions.width(3);
                lineOptions.color(Color.MAGENTA);
            } else {
                lineOptions.width(3);
                lineOptions.color(Color.RED);
            }
            Log.d("mylog", "onPostExecute lineoptions decoded");
        }

        // Drawing polyline in the Google Map for the i-th route
        if (lineOptions != null) {
            //mMap.addPolyline(lineOptions);
            taskCallback.onTaskDone(lineOptions);

        } else {
            Log.d("mylog", "without Polylines drawn");
        }
    }
}

Por 9.99€ al mes tendrás acceso completo a todos los cursos. Sin matrícula ni permanencia.