Conceitos avançados

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

Aquisição de dados

Há diversas maneiras de se obter dados de localização coletados. Aqui, descrevemos duas técnicas para adquirir dados para uso com o recurso Snap to Roads do Roads API.

GPX

GPX é um formato aberto baseado em XML para compartilhar rotas, trilhas e waypoints capturados por dispositivos GPS. Este exemplo usa o analisador XMLPull, um analisador de XML leve disponível para ambientes Java e servidor Java.

/**
 * 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;
}

Veja alguns dados brutos de GPX carregados em um mapa.

Dados brutos de GPX em um mapa

Serviços de localização do Android

A melhor maneira de capturar dados de GPS com um dispositivo Android varia de acordo com seu caso de uso. Dê uma olhada no treinamento do Android sobre Como receber atualizações de localização e veja os exemplos de localização do Google Play no GitHub (link em inglês).

Processamento de caminhos longos

Como o recurso Snap to Roads infere o local com base no caminho completo, em vez de pontos individuais, você precisa tomar cuidado ao processar caminhos longos, ou seja, caminhos que excedem o limite de 100 pontos por solicitação.

Para tratar as solicitações individuais como um caminho longo, você precisa incluir uma sobreposição, de modo que os pontos finais da solicitação anterior sejam incluídos como os primeiros pontos da solicitação subsequente. O número de pontos a serem incluídos depende da precisão dos dados. Inclua mais pontos para solicitações de baixa precisão.

Este exemplo usa o cliente Java para serviços do Google Maps para enviar solicitações paginadas e reintegra os dados, incluindo pontos interpolados, à lista retornada.

/**
 * 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;
}

Veja a seguir os dados acima após executar as solicitações de ajuste da via. A linha vermelha representa os dados brutos e a azul, os dados do alinhamento.

Exemplo de dados coletados nas vias

Uso eficiente da cota

A resposta a uma solicitação de Snap to Roads inclui uma lista de IDs de lugar que mapeiam os pontos que você forneceu, possivelmente com pontos adicionais se você definir interpolate=true.

Para fazer uso eficiente da sua cota permitida para uma solicitação de limites de velocidade, consulte apenas os IDs de local exclusivos em sua solicitação. Este exemplo usa o cliente Java para serviços do Google Maps para consultar limites de velocidade em uma lista de IDs de lugar.

/**
 * 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;
}

Veja a seguir os dados acima com os limites de velocidade marcados em cada ID de lugar exclusivo.

Placas de limite de velocidade em um mapa

Interação com outras APIs

Um dos benefícios de retornar IDs de lugar nas respostas de Snap to Roads é que você pode usá-lo em várias APIs da Plataforma Google Maps. Este exemplo usa o cliente Java para serviços do Google Maps para geocodificar um local retornado da solicitação de alinhamento a estrada acima.

/**
 * 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;
}

Aqui o marcador de limite de velocidade foi anotado com o endereço da API Geocoding.

Endereço geocodificado exibido em um marcador

Código de amostra

Considerações

O código compatível com este artigo está disponível como um único app Android para fins ilustrativos. Na prática, você não deve distribuir suas chaves de API do lado do servidor em um app Android, porque sua chave não pode ser protegida contra acesso não autorizado de terceiros. Em vez disso, para proteger as chaves, implante o código voltado para a API como um proxy do lado do servidor e faça com que o app Android envie solicitações por meio do proxy, garantindo que as solicitações sejam autorizadas.

Download

Faça o download do código no GitHub.