Thử thế hệ tính năng Ma trận khoảng cách tiếp theo bằng API Tuyến đường.

Các phương pháp hay nhất khi sử dụng dịch vụ web ma trận khoảng cách

Dịch vụ web của Google Maps Platform là một tập hợp các giao diện HTTP đối với các dịch vụ của Google, cung cấp dữ liệu địa lý cho các ứng dụng bản đồ của bạn.

Hướng dẫn này mô tả một số phương pháp phổ biến hữu ích cho việc thiết lập yêu cầu dịch vụ web và xử lý các phản hồi dịch vụ. Tham khảo hướng dẫn dành cho nhà phát triển để biết toàn bộ tài liệu về API ma trận khoảng cách.

Dịch vụ web là gì?

Các dịch vụ web của Nền tảng Google Maps là một giao diện để yêu cầu dữ liệu API Maps từ các dịch vụ bên ngoài và sử dụng dữ liệu trong các ứng dụng Maps. Các dịch vụ này được thiết kế để sử dụng cùng với bản đồ theo Các quy định hạn chế về giấy phép trong Điều khoản dịch vụ của Nền tảng Google Maps.

Dịch vụ web API của Maps sử dụng yêu cầu HTTP(S) tới các URL cụ thể, chuyển tham số URL và/hoặc dữ liệu POST định dạng JSON làm đối số cho các dịch vụ. Nhìn chung, các dịch vụ này trả về dữ liệu trong nội dung phản hồi dưới dạng JSON hoặc XML để phân tích cú pháp và/hoặc xử lý.

Yêu cầu API ma trận khoảng cách điển hình thường có dạng sau:

https://maps.googleapis.com/maps/api/distancematrix/output?parameters

trong đó output cho biết định dạng phản hồi (thường là json hoặc xml).

Lưu ý: Tất cả các ứng dụng API ma trận từ xa đều yêu cầu xác thực. Tìm hiểu thêm thông tin về thông tin xác thực.

Truy cập SSL/TLS

HTTPS là bắt buộc đối với tất cả các yêu cầu Nền tảng Google Maps sử dụng khóa API hoặc chứa dữ liệu người dùng. Yêu cầu thực hiện qua HTTP có chứa dữ liệu nhạy cảm có thể bị từ chối.

Tạo URL hợp lệ

Bạn có thể nghĩ rằng một URL "hợp lệ" là điều hiển nhiên, nhưng không hẳn như vậy. Ví dụ: URL được nhập trong thanh địa chỉ trong trình duyệt có thể chứa các ký tự đặc biệt (ví dụ: "上海+中國"); trình duyệt cần phải dịch nội bộ các ký tự đó sang một mã hóa khác trước khi truyền. Tương tự như vậy, bất kỳ mã nào tạo hoặc chấp nhận dữ liệu đầu vào UTF-8 đều có thể coi các URL có ký tự UTF-8 là "hợp lệ", nhưng cũng cần phải dịch các ký tự đó trước khi gửi đến máy chủ web. Quá trình này được gọi là mã hóa URL hoặc mã hóa phần trăm.

Các ký tự đặc biệt

Chúng tôi cần dịch các ký tự đặc biệt vì tất cả các URL đều phải tuân thủ cú pháp được chỉ định trong quy cách Mã nhận dạng tài nguyên đồng nhất (URI). Trên thực tế, điều này có nghĩa là URL chỉ được chứa một tập hợp con ký tự ASCII đặc biệt: ký hiệu chữ và số quen thuộc và một số ký tự dành riêng để sử dụng làm ký tự điều khiển trong URL. Bảng này tóm tắt các ký tự sau:

Tóm tắt các ký tự URL hợp lệ
Đặtký tựSử dụng URL
Chữ và số a b c d e f g h i j k l m n o p q r s t u v w x y z Chuỗi văn bản, sử dụng lược đồ (http), cổng (8080), v.v.
Không dành riêng - _ . ~ Chuỗi văn bản
Đã đặt chỗ ! * ' ( ) ; : @ & = + $ , / ? % # [ ] Ký tự điều khiển và/hoặc Chuỗi văn bản

Khi tạo một URL hợp lệ, bạn phải đảm bảo rằng URL đó chỉ chứa các ký tự xuất hiện trong bảng Tóm tắt các ký tự URL hợp lệ. Việc sử dụng cùng một URL để tạo bộ ký tự này thường dẫn đến 2 vấn đề: một là bỏ qua và một là thay thế:

  • Các ký tự mà bạn muốn xử lý tồn tại bên ngoài tập hợp trên. Ví dụ: các ký tự bằng tiếng nước ngoài như 上海+中國 cần được mã hóa bằng các ký tự trên. Theo quy ước phổ biến, dấu cách (không được cho phép trong URL) thường được biểu thị bằng ký tự dấu cộng '+'.
  • Ký tự tồn tại trong bộ ở trên được đặt làm ký tự dành riêng, nhưng cần phải được sử dụng theo nghĩa đen. Ví dụ: ? được sử dụng trong các URL để cho biết phần đầu của chuỗi truy vấn. Nếu muốn sử dụng chuỗi "? và The Mysterions", bạn cần phải mã hóa ký tự '?'.

Tất cả các ký tự được mã hoá URL đều được mã hoá bằng ký tự '%' và giá trị hex 2 ký tự tương ứng với ký tự UTF-8. Ví dụ: 上海+中國 trong UTF-8 sẽ được mã hóa URL là %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B. Chuỗi ? and the Mysterians sẽ được mã hoá URL là %3F+and+the+Mysterians hoặc %3F%20and%20the%20Mysterians.

Ký tự phổ biến cần mã hóa

Một số ký tự phổ biến phải được mã hóa là:

Ký tự không an toàn Giá trị được mã hóa
Vũ trụ %20
" %22
< %3C
> %3E
# %23
% %25
| %7C

Đôi khi, việc chuyển đổi URL mà bạn nhận được từ hoạt động đầu vào của người dùng là một quy trình phức tạp. Ví dụ: người dùng có thể nhập địa chỉ là "5th&Main St." Nhìn chung, bạn nên xây dựng URL từ các phần của URL, coi mọi dữ liệu người dùng nhập vào là ký tự chữ.

Ngoài ra, URL được giới hạn 8192 ký tự cho tất cả các dịch vụ web của Google Maps Platform và API web tĩnh. Đối với hầu hết các dịch vụ, giới hạn ký tự này hiếm khi đạt đến. Tuy nhiên, hãy lưu ý rằng một số dịch vụ nhất định có nhiều tham số có thể dẫn đến URL dài.

Sử dụng nhẹ các API của Google

Ứng dụng API được thiết kế kém có thể đặt tải nhiều hơn mức cần thiết trên cả máy chủ Internet và máy chủ của Google. Phần này trình bày một số phương pháp hay nhất dành cho ứng dụng API. Việc làm theo các phương pháp hay nhất này có thể giúp bạn tránh việc ứng dụng của mình bị chặn do vô tình sử dụng các API này.

Thuật toán thời gian đợi luỹ thừa

Trong một số ít trường hợp, có thể xảy ra sự cố khi phân phát yêu cầu của bạn; bạn có thể nhận được mã phản hồi HTTP 4XX hoặc 5XX, hoặc kết nối TCP có thể chỉ gặp sự cố ở đâu đó giữa máy khách và máy chủ của Google. Thông thường, bạn nên thử lại yêu cầu vì yêu cầu tiếp theo có thể thành công khi yêu cầu ban đầu không thành công. Tuy nhiên, điều quan trọng là không chỉ lặp lại yêu cầu đến các máy chủ của Google. Hành vi lặp lại này có thể làm quá tải mạng khách hàng của bạn và Google, gây ra nhiều sự cố cho nhiều bên.

Cách tiếp cận tốt hơn là thử lại với độ trễ tăng dần giữa các lần thử. Thông thường, độ trễ tăng lên theo một hệ số nhân với mỗi lần thử, một phương pháp được gọi là Độ trễ thời gian.

Ví dụ: hãy xem xét một ứng dụng muốn gửi yêu cầu này đến Múi giờ:

https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510&timestamp=1331161200&key=YOUR_API_KEY

Ví dụ sau đây về Python cho thấy cách gửi yêu cầu bằng thuật toán thời gian đợi luỹ thừa:

import json
import time
import urllib.error
import urllib.parse
import urllib.request

# The maps_key defined below isn't a valid Google Maps API key.
# You need to get your own API key.
# See https://developers.google.com/maps/documentation/timezone/get-api-key
API_KEY = "YOUR_KEY_HERE"
TIMEZONE_BASE_URL = "https://maps.googleapis.com/maps/api/timezone/json"


def timezone(lat, lng, timestamp):

    # Join the parts of the URL together into one string.
    params = urllib.parse.urlencode(
        {"location": f"{lat},{lng}", "timestamp": timestamp, "key": API_KEY,}
    )
    url = f"{TIMEZONE_BASE_URL}?{params}"

    current_delay = 0.1  # Set the initial retry delay to 100ms.
    max_delay = 5  # Set the maximum retry delay to 5 seconds.

    while True:
        try:
            # Get the API response.
            response = urllib.request.urlopen(url)
        except urllib.error.URLError:
            pass  # Fall through to the retry loop.
        else:
            # If we didn't get an IOError then parse the result.
            result = json.load(response)

            if result["status"] == "OK":
                return result["timeZoneId"]
            elif result["status"] != "UNKNOWN_ERROR":
                # Many API errors cannot be fixed by a retry, e.g. INVALID_REQUEST or
                # ZERO_RESULTS. There is no point retrying these requests.
                raise Exception(result["error_message"])

        if current_delay > max_delay:
            raise Exception("Too many retry attempts.")

        print("Waiting", current_delay, "seconds before retrying.")

        time.sleep(current_delay)
        current_delay *= 2  # Increase the delay each time we retry.


if __name__ == "__main__":
    tz = timezone(39.6034810, -119.6822510, 1331161200)
    print(f"Timezone: {tz}")

Bạn cũng nên lưu ý không có mã thử lại nào cao hơn trong chuỗi lệnh gọi ứng dụng, dẫn đến các yêu cầu lặp lại liên tiếp.

Yêu cầu được đồng bộ hóa

Một số lượng lớn các yêu cầu được đồng bộ hóa đến các API của Google có thể trông giống như một cuộc tấn công Từ chối dịch vụ phân tán (DDoS) nhằm vào cơ sở hạ tầng của Google và được xử lý tương ứng. Để tránh điều này, bạn nên đảm bảo rằng các yêu cầu API không được đồng bộ hoá giữa các ứng dụng.

Ví dụ: xem xét một ứng dụng hiển thị thời gian theo múi giờ hiện tại. Ứng dụng này có thể sẽ đặt báo thức trong hệ điều hành máy khách để đánh thức đồng hồ vào đầu phút để có thể cập nhật thời gian hiển thị. Ứng dụng không nên thực hiện bất kỳ lệnh gọi API nào trong quá trình xử lý liên kết với chuông báo đó.

Việc gọi API để phản hồi một cảnh báo cố định không phù hợp vì dẫn đến việc các lệnh gọi API được đồng bộ hoá vào đầu phút, ngay cả giữa các thiết bị khác nhau, thay vì được phân phối đồng đều theo thời gian. Ứng dụng có thiết kế kém hiệu quả sẽ tạo ra mức tăng đột biến về lưu lượng truy cập ở mức 60 lần mức bình thường vào đầu mỗi phút.

Thay vào đó, bạn có thể đặt báo thức thứ hai thành thời gian được chọn ngẫu nhiên. Khi chuông báo thứ hai kích hoạt, ứng dụng sẽ gọi bất kỳ API nào cần thiết và lưu trữ kết quả. Khi muốn cập nhật màn hình vào đầu phút, ứng dụng sẽ sử dụng kết quả đã lưu trữ trước đó thay vì gọi lại API. Với phương pháp này, các lệnh gọi API được trải đều theo thời gian. Hơn nữa, các lệnh gọi API không làm trì hoãn việc kết xuất khi màn hình đang được cập nhật.

Ngoài thời gian bắt đầu của phút, các thời gian đồng bộ hóa phổ biến khác mà bạn nên c không nên nhắm mục tiêu là vào thời điểm bắt đầu một giờ và bắt đầu mỗi ngày vào lúc nửa đêm.

Đang xử lý phản hồi

Phần này thảo luận cách trích xuất động các giá trị này từ phản hồi dịch vụ web.

Các dịch vụ web của Google Maps cung cấp câu trả lời dễ hiểu nhưng không chính xác là thân thiện với người dùng. Khi thực hiện truy vấn, thay vì hiển thị một tập dữ liệu, bạn có thể muốn trích xuất một số giá trị cụ thể. Nhìn chung, bạn sẽ muốn phân tích cú pháp các phản hồi từ dịch vụ web và chỉ trích xuất những giá trị mà bạn quan tâm.

Giao thức phân tích cú pháp mà bạn sử dụng phụ thuộc vào việc bạn trả về kết quả ở định dạng XML hoặc JSON. Phản hồi JSON, đã ở dạng đối tượng JavaScript, có thể được xử lý trong chính JavaScript trên ứng dụng. Phản hồi XML phải được xử lý bằng bộ xử lý XML và ngôn ngữ truy vấn XML để xử lý các phần tử trong định dạng XML. Chúng tôi sử dụng XPath trong các ví dụ sau, vì định dạng này thường được hỗ trợ trong thư viện xử lý XML.

Xử lý XML bằng XPath

XML là một định dạng thông tin có cấu trúc tương đối ổn định dùng để trao đổi dữ liệu. Mặc dù không nhẹ bằng JSON, nhưng XML cung cấp hỗ trợ nhiều ngôn ngữ hơn và các công cụ mạnh mẽ hơn. Ví dụ: mã để xử lý XML trong Java được tích hợp vào các gói javax.xml.

Khi xử lý các phản hồi XML, bạn nên sử dụng ngôn ngữ truy vấn phù hợp để chọn các nút trong tài liệu XML, thay vì giả định rằng các phần tử nằm ở vị trí tuyệt đối trong mã đánh dấu XML. XPath là một cú pháp ngôn ngữ dùng để mô tả riêng các nút và phần tử trong một tài liệu XML. Biểu thức XPath cho phép bạn xác định nội dung cụ thể trong tài liệu phản hồi XML.

Biểu thức XPath

Một số hiểu biết với XPath có ý nghĩa rất lớn trong việc phát triển một lược đồ phân tích cú pháp mạnh mẽ. Phần này sẽ tập trung vào cách xử lý các phần tử trong tài liệu XML bằng XPath, cho phép bạn xử lý nhiều phần tử và tạo các truy vấn phức tạp.

XPath sử dụng biểu thức để chọn các phần tử trong tài liệu XML, sử dụng cú pháp tương tự như cú pháp dùng cho đường dẫn thư mục. Các biểu thức này xác định các phần tử trong cây tài liệu XML, đây là cây phân cấp tương tự như cây phân cấp. Nhìn chung, biểu thức XPath là tham số, cho biết rằng biểu thức này sẽ khớp với tất cả các nút khớp với tiêu chí được cung cấp.

Chúng tôi sẽ sử dụng XML trừu tượng sau đây để minh hoạ cho các ví dụ:

<WebServiceResponse>
 <status>OK</status>
 <result>
  <type>sample</type>
  <name>Sample XML</name>
  <location>
   <lat>37.4217550</lat>
   <lng>-122.0846330</lng>
  </location>
 </result>
 <result>
  <message>The secret message</message>
 </result>
</WebServiceResponse>

Lựa chọn nút trong biểu thức

Các lựa chọn XPath chọn nút. Nút gốc bao gồm toàn bộ tài liệu. Bạn chọn nút này bằng cách sử dụng biểu thức đặc biệt "/". Xin lưu ý rằng nút gốc không phải là nút cấp cao nhất của tài liệu XML; trên thực tế, nút này nằm trên một cấp ở trên phần tử cấp cao nhất này và bao gồm nó.

Nút phần tử đại diện cho các phần tử khác nhau trong cây tài liệu XML. Ví dụ: phần tử <WebServiceResponse> đại diện cho phần tử cấp cao nhất được trả về trong dịch vụ mẫu của chúng tôi ở trên. Bạn chọn từng nút thông qua đường dẫn tuyệt đối hoặc tương đối, được biểu thị bằng sự hiện diện hoặc không có ký tự "/" ở đầu.

  • Đường dẫn tuyệt đối: biểu thức "/WebServiceResponse/result" chọn tất cả các nút <result> là nút con của nút <WebServiceResponse>. (Lưu ý rằng cả hai phần tử này đều xuất phát từ nút gốc "/").
  • Đường dẫn tương đối từ ngữ cảnh hiện tại: biểu thức "result" sẽ khớp với mọi phần tử <result> trong ngữ cảnh hiện tại. Nhìn chung, bạn không cần lo lắng về ngữ cảnh vì thường xử lý kết quả dịch vụ web thông qua một biểu thức duy nhất.

Một trong hai biểu thức này có thể được tăng cường thông qua việc thêm đường dẫn ký tự đại diện, được biểu thị bằng dấu gạch chéo đôi ("//"). Ký tự đại diện này cho biết không có hoặc nhiều phần tử có thể khớp với đường dẫn can thiệp. Ví dụ: biểu thức XPath "//formatted_address" sẽ khớp với tất cả các nút của tên đó trong tài liệu hiện tại. Biểu thức //viewport//lat sẽ khớp với tất cả các phần tử <lat> có thể theo dõi <viewport> dưới dạng phần tử mẹ.

Theo mặc định, biểu thức XPath khớp với tất cả các phần tử. Bạn có thể hạn chế biểu thức để khớp với một phần tử nhất định bằng cách cung cấp một hàm liên kết nằm trong dấu ngoặc vuông ([]). Ví dụ: biểu thức XPath "/GeocodeResponse/result[2] luôn trả về kết quả thứ hai.

Loại biểu thức
Root node
Biểu thức XPath:  "/"
Lựa chọn:
    <WebServiceResponse>
     <status>OK</status>
     <result>
      <type>sample</type>
      <name>Sample XML</name>
      <location>
       <lat>37.4217550</lat>
       <lng>-122.0846330</lng>
      </location>
     </result>
     <result>
      <message>The secret message</message>
     </result>
    </WebServiceResponse>
    
Đường dẫn tuyệt đối
Biểu thức XPath:  "/WebServiceResponse/result"
Lựa chọn:
    <result>
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    </result>
    <result>
     <message>The secret message</message>
    </result>
    
Đường dẫn có ký tự đại diện
Biểu thức XPath:  "/WebServiceResponse//location"
Lựa chọn:
    <location>
     <lat>37.4217550</lat>
     <lng>-122.0846330</lng>
    </location>
    
Đường dẫn có vị từ
Biểu thức XPath:  "/WebServiceResponse/result[2]/message"
Lựa chọn:
    <message>The secret message</message>
    
Tất cả các con trực tiếp của result đầu tiên
Biểu thức XPath:  "/WebServiceResponse/result[1]/*"
Lựa chọn:
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    
name của một result có văn bản type là "mẫu".
Biểu thức XPath:  "/WebServiceResponse/result[type/text()='sample']/name"
Lựa chọn:
    Sample XML
    

Điều quan trọng cần lưu ý là khi chọn các phần tử, bạn phải chọn các nút, chứ không chỉ văn bản trong các đối tượng đó. Nhìn chung, bạn nên lặp lại trên tất cả các nút phù hợp và trích xuất văn bản. Bạn cũng có thể trực tiếp so khớp các nút văn bản; xem Nút văn bản bên dưới.

Xin lưu ý rằng XPath cũng hỗ trợ các nút thuộc tính; tuy nhiên, tất cả các dịch vụ web của Google Maps đều phân phối các phần tử không có thuộc tính, vì vậy việc so khớp các thuộc tính là không cần thiết.

Lựa chọn văn bản trong biểu thức

Văn bản trong tài liệu XML được chỉ định trong biểu thức XPath thông qua toán tử nút văn bản. Toán tử "text()" này cho biết việc trích xuất văn bản từ nút đã chỉ định. Ví dụ: biểu thức XPath "//formatted_address/text()" sẽ trả về tất cả văn bản trong các phần tử <formatted_address>.

Loại biểu thức
Tất cả các nút văn bản (bao gồm cả khoảng trắng)
Biểu thức XPath:  "//text()"
Lựa chọn:
    sample
    Sample XML

    37.4217550
    -122.0846330
    The secret message
    
Chọn văn bản
Biểu thức XPath:  "/WebServiceRequest/result[2]/message/text()"
Lựa chọn:
    The secret message
    
Lựa chọn nhạy cảm theo ngữ cảnh
Biểu thức XPath:  "/WebServiceRequest/result[type/text() = 'sample']/name/text()"
Lựa chọn:
    Sample XML
    

Ngoài ra, bạn có thể đánh giá một biểu thức và trả về một tập hợp các nút, sau đó lặp lại "nút đó" để trích xuất văn bản từ mỗi nút. Chúng tôi sử dụng phương pháp này trong ví dụ bên dưới.

Để biết thêm thông tin về XPath, hãy tham khảo Đặc tả W3C của XPath.

Đánh giá XPath trong Java

Java có hỗ trợ rộng rãi để phân tích cú pháp XML và sử dụng biểu thức XPath trong gói javax.xml.xpath.*. Vì lý do đó, mã mẫu trong phần này sẽ sử dụng Java để minh hoạ cách xử lý XML và phân tích cú pháp dữ liệu từ phản hồi dịch vụ XML.

Để sử dụng XPath trong mã Java, trước tiên, bạn cần tạo một thực thể của XPathFactory và gọi newXPath() trên nhà máy đó để tạo đối tượng XPath . Sau đó, đối tượng này có thể xử lý các biểu thức XML và XPath đã chuyển bằng cách sử dụng phương thức evaluate().

Khi đánh giá biểu thức XPath, hãy đảm bảo rằng bạn lặp lại bất kỳ "tập hợp nút" nào có thể được trả về. Vì các kết quả này được trả về dưới dạng các nút DOM trong mã Java, nên bạn cần thu thập nhiều giá trị như vậy trong một đối tượng NodeList và lặp lại trên đối tượng đó để trích xuất mọi văn bản hoặc giá trị từ các nút đó.

Mã sau đây minh hoạ cách tạo một đối tượng XPath, gán đối tượng XML và biểu thức XPath, cũng như đánh giá biểu thức để in nội dung liên quan.

import org.xml.sax.InputSource;
import org.w3c.dom.*;
import javax.xml.xpath.*;
import java.io.*;

public class SimpleParser {

  public static void main(String[] args) throws IOException {

	XPathFactory factory = XPathFactory.newInstance();

    XPath xpath = factory.newXPath();

    try {
      System.out.print("Web Service Parser 1.0\n");

      // In practice, you'd retrieve your XML via an HTTP request.
      // Here we simply access an existing file.
      File xmlFile = new File("XML_FILE");

      // The xpath evaluator requires the XML be in the format of an InputSource
	  InputSource inputXml = new InputSource(new FileInputStream(xmlFile));

      // Because the evaluator may return multiple entries, we specify that the expression
      // return a NODESET and place the result in a NodeList.
      NodeList nodes = (NodeList) xpath.evaluate("XPATH_EXPRESSION", inputXml, XPathConstants.NODESET);

      // We can then iterate over the NodeList and extract the content via getTextContent().
      // NOTE: this will only return text for element nodes at the returned context.
      for (int i = 0, n = nodes.getLength(); i < n; i++) {
        String nodeString = nodes.item(i).getTextContent();
        System.out.print(nodeString);
        System.out.print("\n");
      }
    } catch (XPathExpressionException ex) {
	  System.out.print("XPath Error");
    } catch (FileNotFoundException ex) {
      System.out.print("File Error");
    }
  }
}