Conceptos avanzados

Adquirir datos

Hay muchas formas de obtener datos de ubicación recopilados. Aquí describimos dos técnicas de adquisición de datos para utilizar con la función ajuste a rutas de el Roads API.

GPX

GPX es un formato abierto basado en XML para compartir rutas, caminos y puntos de referencia capturadas por dispositivos GPS. En este ejemplo, se usa el analizador XmlPull, un Analizador XML ligero disponible para entornos de servidor Java y móviles.

/**
 * Parses the waypoint (wpt tags) data into native objects from a GPX stream.
 */
private List<LatLng> loadGpxData(XmlPullParser parser, InputStream gpxIn)
        throws XmlPullParserException, IOException {
    // We use a List<> as we need subList for paging later
    List<LatLng> latLngs = new ArrayList<>();
    parser.setInput(gpxIn, null);
    parser.nextTag();

    while (parser.next() != XmlPullParser.END_DOCUMENT) {
        if (parser.getEventType() != XmlPullParser.START_TAG) {
            continue;
        }

        if (parser.getName().equals("wpt")) {
            // Save the discovered latitude/longitude attributes in each <wpt>.
            latLngs.add(new LatLng(
                    Double.valueOf(parser.getAttributeValue(null, "lat")),
                    Double.valueOf(parser.getAttributeValue(null, "lon"))));
        }
        // Otherwise, skip irrelevant data
    }

    return latLngs;
}

Estos son algunos datos GPX sin procesar cargados en un mapa.

Datos GPX sin procesar en un mapa

Servicios de ubicación de Android

La mejor manera de capturar datos de GPS desde un dispositivo Android varía según tu para tu caso de uso específico. Consulta la clase de capacitación de Android sobre la recepción de la ubicación. Actualizaciones, así como los ejemplos de ubicación de Google Play en GitHub.

Cómo procesar rutas largas

Dado que la función ajustar a las rutas infiere la ubicación en función de la ruta completa, en lugar de puntos individuales, debes tener cuidado al procesar datos rutas de acceso (es decir, rutas que exceden el límite de 100 puntos por solicitud).

Para tratar las solicitudes individuales como una ruta larga, debes incluir se superponen, de modo que se incluyan los puntos finales de la solicitud anterior. como los primeros puntos de la siguiente solicitud. La cantidad de puntos que se deben incluir depende de la exactitud de tus datos. Deberías incluir más puntos para solicitudes de baja precisión.

En este ejemplo, se usa Java Client para los servicios de Google Maps a fin de enviar solicitudes paginadas y y, luego, vuelve a unir los datos, incluidos los puntos interpolados, a la lista que se devuelve.

/**
 * Snaps the points to their most likely position on roads using the Roads API.
 */
private List<SnappedPoint> snapToRoads(GeoApiContext context) throws Exception {
    List<SnappedPoint> snappedPoints = new ArrayList<>();

    int offset = 0;
    while (offset < mCapturedLocations.size()) {
        // Calculate which points to include in this request. We can't exceed the API's
        // maximum and we want to ensure some overlap so the API can infer a good location for
        // the first few points in each request.
        if (offset > 0) {
            offset -= PAGINATION_OVERLAP;   // Rewind to include some previous points.
        }
        int lowerBound = offset;
        int upperBound = Math.min(offset + PAGE_SIZE_LIMIT, mCapturedLocations.size());

        // Get the data we need for this page.
        LatLng[] page = mCapturedLocations
                .subList(lowerBound, upperBound)
                .toArray(new LatLng[upperBound - lowerBound]);

        // Perform the request. Because we have interpolate=true, we will get extra data points
        // between our originally requested path. To ensure we can concatenate these points, we
        // only start adding once we've hit the first new point (that is, skip the overlap).
        SnappedPoint[] points = RoadsApi.snapToRoads(context, true, page).await();
        boolean passedOverlap = false;
        for (SnappedPoint point : points) {
            if (offset == 0 || point.originalIndex >= PAGINATION_OVERLAP - 1) {
                passedOverlap = true;
            }
            if (passedOverlap) {
                snappedPoints.add(point);
            }
        }

        offset = upperBound;
    }

    return snappedPoints;
}

Aquí se muestran los datos anteriores después de ejecutar las solicitudes de ajuste a rutas. El ícono rojo la línea representa los datos sin procesar y la azul, los datos ajustados.

Ejemplo de datos que se ajustan a las rutas

Uso eficiente de la cuota

La respuesta a una solicitud de ajuste a las rutas incluye una lista de los IDs de lugar. que se asignan a los puntos proporcionados, potencialmente con puntos adicionales si establecer interpolate=true.

Para hacer un uso eficiente de tu cuota permitida en una solicitud de límites de velocidad, solo debes consultar IDs de lugar únicos en tu solicitud. En este ejemplo, se usa el cliente de Java para los servicios de Google Maps para consultar los límites de velocidad de una lista de lugares de sus IDs.

/**
 * Retrieves speed limits for the previously-snapped points. This method is efficient in terms
 * of quota usage as it will only query for unique places.
 *
 * Note: Speed limit data is only available for requests using an API key enabled for a
 * Google Maps APIs Premium Plan license.
 */
private Map<String, SpeedLimit> getSpeedLimits(GeoApiContext context, List<SnappedPoint> points)
        throws Exception {
    Map<String, SpeedLimit> placeSpeeds = new HashMap<>();

    // Pro tip: Save on quota by filtering to unique place IDs.
    for (SnappedPoint point : points) {
        placeSpeeds.put(point.placeId, null);
    }

    String[] uniquePlaceIds =
            placeSpeeds.keySet().toArray(new String[placeSpeeds.keySet().size()]);

    // Loop through the places, one page (API request) at a time.
    for (int i = 0; i < uniquePlaceIds.length; i += PAGE_SIZE_LIMIT) {
        String[] page = Arrays.copyOfRange(uniquePlaceIds, i,
                Math.min(i + PAGE_SIZE_LIMIT, uniquePlaceIds.length));

        // Execute!
        SpeedLimit[] placeLimits = RoadsApi.speedLimits(context, page).await();
        for (SpeedLimit sl : placeLimits) {
            placeSpeeds.put(sl.placeId, sl);
        }
    }

    return placeSpeeds;
}

A continuación, se muestran los datos anteriores con los límites de velocidad marcados en cada ID de lugar único.

Señales de límite de velocidad en un mapa

Interactúa con otras APIs

Uno de los beneficios de tener IDs de lugar que se devuelven en la página de ajuste a las rutas es que puedes usar el ID de lugar en muchos de los APIs de Google Maps Platform. En este ejemplo, se usa Java Client para los servicios de Google Maps. para geocodificar un sitio devuelto a partir de la solicitud de ajuste a ruta anterior.

/**
 * Geocodes a snapped point using the place ID.
 */
private GeocodingResult geocodeSnappedPoint(GeoApiContext context, SnappedPoint point) throws Exception {
    GeocodingResult[] results = GeocodingApi.newRequest(context)
            .place(point.placeId)
            .await();

    if (results.length > 0) {
        return results[0];
    }
    return null;
}

Aquí, el marcador de límite de velocidad se anotó con la dirección de la la API de Geocoding.

Dirección geocodificada en un marcador

Código de muestra

Consideraciones

El código que respalda este artículo está disponible como una sola app para Android con fines ilustrativos. En la práctica, no debes distribuir tus recursos claves de API en una app para Android, ya que no se pueden proteger contra ataques el acceso de un tercero. En su lugar, para proteger tus claves, debes implementar el Código orientado a la API como un proxy del servidor y haz que tu app para Android envíe solicitudes a través del proxy, lo que garantiza que las solicitudes estén autorizadas.

Descargar

Descarga el código desde GitHub.