Khái niệm nâng cao

Đang nhận dữ liệu

Có nhiều cách để có được dữ liệu vị trí đã thu thập. Ở đây, chúng tôi mô tả 2 kỹ thuật thu thập dữ liệu để sử dụng bằng tính năng gắn liền với đường của Roads API.

GPX

GPX là một định dạng dựa trên XML mở để chia sẻ các tuyến đường, tuyến đường và điểm tham chiếu mà thiết bị GPS thu thập được. Ví dụ này sử dụng trình phân tích cú pháp XmlPull, một trình phân tích cú pháp XML gọn nhẹ có sẵn cho cả máy chủ Java và môi trường thiết bị di động.

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

Sau đây là một số dữ liệu GPX thô được tải lên bản đồ.

Dữ liệu GPX thô trên bản đồ

Dịch vụ vị trí của Android

Cách tốt nhất để thu thập dữ liệu GPS từ thiết bị Android sẽ khác nhau tuỳ theo trường hợp sử dụng của bạn. Hãy xem lớp đào tạo Android về cách Nhận thông tin cập nhật vị trí, cũng như Mẫu Vị trí trên Google Play trên GitHub.

Xử lý đường dẫn dài

Vì tính năng gắn liền với đường suy luận vị trí dựa trên đường dẫn đầy đủ, thay vì từng điểm riêng lẻ, nên bạn cần chú ý khi xử lý đường dẫn dài (tức là các đường dẫn vượt quá giới hạn 100 điểm cho mỗi yêu cầu).

Để coi các yêu cầu riêng lẻ là một đường dẫn dài, bạn nên thêm một số điểm trùng lặp, chẳng hạn như các điểm cuối cùng của yêu cầu trước đó được đưa vào làm điểm đầu tiên của yêu cầu tiếp theo. Số điểm cần đưa vào phụ thuộc vào độ chính xác của dữ liệu. Bạn nên thêm nhiều điểm hơn cho các yêu cầu có độ chính xác thấp.

Ví dụ này sử dụng Ứng dụng Java dành cho Dịch vụ Google Maps để gửi các yêu cầu được phân trang, sau đó tham gia lại dữ liệu, bao gồm cả các điểm nội suy, vào danh sách được trả về.

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

Dưới đây là dữ liệu ở trên sau khi chạy nhanh các yêu cầu về đường đi. Đường màu đỏ là dữ liệu thô và đường màu xanh dương là dữ liệu được chụp nhanh.

Ví dụ về dữ liệu được điều chỉnh theo đường

Sử dụng hạn mức hiệu quả

Phản hồi cho yêu cầu gắn liền với đường sẽ bao gồm một danh sách mã địa điểm liên kết với các điểm mà bạn đã cung cấp, có thể kèm theo điểm bổ sung nếu bạn đặt interpolate=true.

Để sử dụng hiệu quả hạn mức được phép cho yêu cầu giới hạn tốc độ, bạn chỉ nên truy vấn mã địa điểm duy nhất trong yêu cầu của mình. Ví dụ này sử dụng Ứng dụng Java dành cho Dịch vụ Google Maps để truy vấn giới hạn tốc độ từ danh sách mã địa điểm.

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

Dưới đây là dữ liệu ở trên, trong đó có giới hạn tốc độ được đánh dấu tại mỗi mã địa điểm duy nhất.

Biển báo giới hạn tốc độ trên bản đồ

Tương tác với các API khác

Một trong những lợi ích của việc trả về mã địa điểm trong các phản hồi gắn liền với đường là bạn có thể sử dụng mã địa điểm trên nhiều API Nền tảng Google Maps. Ví dụ này sử dụng Ứng dụng Java dành cho Dịch vụ Google Maps để mã hoá địa lý một địa điểm được trả về từ ảnh chụp nhanh ở trên vào yêu cầu về đường.

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

Ở đây, điểm đánh dấu giới hạn tốc độ đã được chú thích bằng địa chỉ từ API mã hoá địa lý.

Địa chỉ được mã hoá địa lý hiển thị trên một điểm đánh dấu

Mã mẫu

Những yếu tố nên cân nhắc

Mã hỗ trợ bài viết này được cung cấp dưới dạng một ứng dụng Android duy nhất nhằm mục đích minh hoạ. Trên thực tế, bạn không nên phân phối các khoá API phía máy chủ trong một ứng dụng Android vì khoá của bạn không thể được bảo vệ khỏi quyền truy cập trái phép từ bên thứ ba. Thay vào đó, để bảo mật khoá, bạn nên triển khai mã dành cho API dưới dạng proxy phía máy chủ và yêu cầu ứng dụng Android gửi yêu cầu thông qua proxy, đảm bảo các yêu cầu được uỷ quyền.

Tải xuống

Tải mã nguồn xuống từ GitHub.