Google Maps API ウェブサービス

Google マップ ウェブサービスは、マップ アプリケーションに地理データを提供する Google サービスへの HTTP インターフェースの集合です。このガイドでは、ウェブサービスおよびすべての各種サービスに共通のホスト情報のみを紹介します。各サービスの個別のドキュメントは、次の場所にあります。

必要な API の確認

プロジェクトに適した API を見つけるには、API Picker を使用します。

ウェブサービスの詳細

このガイドではこれ以降、ウェブサービス リクエストを設定したりレスポンスを解析したりするための手法について説明します。ただし、各サービスの特定のドキュメントについては、適切なドキュメントを参照する必要があります。

ウェブサービスとは

Google Maps API は、Maps API データを外部サービスからリクエストしたり、マップ アプリケーション内で使用するためのインターフェースとしてこれらのウェブサービスを提供しています。これらのサービスは、Maps API Terms of Service License Restrictions に記載されているとおり、マップとともに使用するように設計されています。

これらのウェブサービスでは、特定の URL への HTTP リクエストを使用し、URL パラメータを引数としてサービスに渡します。通常、これらのサービスは、アプリケーションで解析または処理できるように、HTTP リクエストのデータを JSON または XML として返します。

一般的なウェブサービス リクエストは、通常は次の形式になります。

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

service はリクエストされた特定のサービスを示し、output はレスポンス形式(通常は json または xml)を示します。

各サービスの詳細については、そのサービスのデベロッパー ガイドをご覧ください。このガイドでは、ウェブサービス リクエストの設定やウェブサービス レスポンスの処理に役立つ一般的な手法を示します。

SSL アクセス

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

HTTPS は、ユーザーデータまたはデベロッパー ID を含むすべての Maps API ウェブサービス リクエストで必須です。HTTP で機密データを含むリクエストを送信すると、拒否される場合があります。

有効な URL の作成

「有効な」URL は説明の必要がないことと考えられるかもしれませんが、そうとは限りません。たとえば、ブラウザのアドレス バーに入力された URL に、特殊文字("上海+中國" など)が含まれている場合があります。ブラウザでは、送信前にこれらの文字を内部的に別のエンコードに変換する必要があります。同様に、UTF-8 入力を生成または受け付けるコードは、UTF-8 文字を含む URL を「有効」として扱うことがありますが、それらの文字をウェブサーバーに送信する前に変換する必要があります。このプロセスは URL エンコードと呼ばれます。

特殊文字を変換する必要があるのは、すべての URL が W3 Uniform Resource Identifier 仕様で規定されている構文に従う必要があるためです。つまり、URL には、ASCII 文字の特別なサブセット(よく使用される英数記号および URL 内で制御文字として使用される予約文字)のみを含める必要があります。次の表にこれらの文字の要約を示します。

有効な URL 文字の要約
セット文字URL での使用法
英数字 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 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 0 1 2 3 4 5 6 7 8 9 テキスト文字列、スキームでの使用(http)、ポート(8080)など
非予約 - _ . ~ テキスト文字列
予約済み ! * ' ( ) ; : @ & = + $ , / ? % # [ ] 制御文字やテキスト文字列

有効な URL を作成する場合は、URL に上記の文字のみを含める必要があります。URL でこの文字セットを使用すると、一般的に 2 つの問題が発生します。1 つは省略、もう 1 つは置き換えです。

  • 処理する文字が上記のセットに含まれない場合。たとえば、上海+中國などの外国語の文字は、上記の文字を使用してエンコードする必要があります。よく知られている規則では、スペース(URL 内では許可されない)は、プラス '+' 文字を使用して表現されることがあります。
  • 文字は上記のセットに予約文字として含まれるが、文字どおりに使用する必要がある場合。たとえば、? は、URL 内でクエリ文字列の先頭を示すために使用されます。文字列「? and the Mysterions」を使用する場合は、'?' 文字をエンコードする必要があります。

URL エンコードされるすべての文字は、'%' 文字と UTF-8 文字に対応する 2 文字の 16 進値を使用してエンコードされます。たとえば、UTF-8 の上海+中國は、%E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B として URL エンコードされます。文字列 ? and the Mysterians は、%3F+and+the+Mysterians として URL エンコードされます。

エンコードする必要がある一般的な文字は次のとおりです。

危険な文字 エンコードされた値
スペース %20
" %22
< %3C
> %3E
# %23
% %25
| %7C

ユーザー入力から受け取った URL の変換が難しい場合があります。たとえば、ユーザーは「5th&Main St.」という住所を入力することがあります。通常は、ユーザー入力をリテラル文字として処理して、URL をパーツから作成する必要があります。

さらに、URL はすべてのウェブサービスで 8192 文字に制限されています。ほとんどのサービスでは、この文字制限に達することはめったにありません。ただし、複数のパラメータを持つ特定のサービスでは、URL が長くなる可能性があります。

Google API の適切な使用

設計上問題がある API クライアントによって、インターネットと Google のサーバーの両方に必要以上の負荷がかかるおそれがあります。このセクションでは、API のクライアントのベスト プラクティスについて説明します。以下のベスト プラクティスに従うと、アプリケーションによる意図しない API の乱用を防ぐことができます。

指数関数的バックオフ

まれに、リクエストに対して正常なレスポンスがない場合があります。たとえば 4XX または 5XX HTTP レスポンス コードが返されたり、クライアントと Google のサーバー間で単純に TCP 接続が失敗したりします。一般的に、最初のリクエストが失敗した後に、続けてリクエストをすると成功する場合があるので、再試行をする価値はあります。ただし、Google のサーバーへのリクエストを単純に繰り返しループさせないことが重要です。このようなループ動作は、クライアントと Google 間のネットワークに多大な負荷をかけ、広い範囲に悪影響を及ぼします。

適切なアプローチは、間隔を増加させながら再試行することです。一般的には、試行するたびに倍数因子をかけて間隔を長くします。このアプローチは指数関数的バックオフと呼ばれます。

たとえば、アプリケーションで Google Maps Time Zone API に対してこのリクエストを実行するケースを考えてみましょう。

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

次の Python の例では、指数関数的バックオフを使用してリクエストを実行する方法を示しています。

import json
import time
import urllib
import urllib2

def timezone(lat, lng, timestamp):
    # 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
    maps_key = 'YOUR_KEY_HERE'
    timezone_base_url = 'https://maps.googleapis.com/maps/api/timezone/json'

    # This joins the parts of the URL together into one string.
    url = timezone_base_url + '?' + urllib.urlencode({
        'location': "%s,%s" % (lat, lng),
        'timestamp': timestamp,
        'key': maps_key,
    })

    current_delay = 0.1  # Set the initial retry delay to 100ms.
    max_delay = 3600  # Set the maximum retry delay to 1 hour.

    while True:
        try:
            # Get the API response.
            response = str(urllib2.urlopen(url).read())
        except IOError:
            pass  # Fall through to the retry loop.
        else:
            # If we didn't get an IOError then parse the result.
            result = json.loads(response.replace('\\n', ''))
            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.

tz = timezone(39.6034810, -119.6822510, 1331161200)
print 'Timezone:', tz

また、アプリケーションの呼び出しチェーンの上位に再試行のコードを書かないように注意する必要があります。上位に書くと、リクエストを立て続けに繰り返し実行することになります。

同期されたリクエスト

Google API に対して同期されたリクエストを大量に送信すると、Google のインフラストラクチャへの DDoS(分散サービス妨害)攻撃のように見えるため、そのように判断されて処理されます。これを回避するには、クライアント間で API リクエストの同期をとらないようにする必要があります。

例として、現在のタイムゾーンの時刻を表示するアプリケーションについて考えてみましょう。このアプリケーションは、表示時刻を更新できるように、クライアントのオペレーティング システムで毎分 0 秒に起動するアラームを設定するものとします。アプリケーションでは、このアラーム関連の処理に、API 呼び出しを含めないようにする必要があります。

定刻のアラームへのレスポンスで API 呼び出しを実行すると、時間的に均等になるように台数を分散させることなく、すべての端末で毎分 0 秒に一斉に API 呼び出しが実行されるため好ましくありません。設計上問題があり、このような処理を行うアプリケーションは、毎分 0 秒にトラフィックを通常の 60 倍にまで上昇させてしまいます。

改善案の 1 つとして、ランダムに選択した時刻に 2 つ目のアラームを設定できる設計にすると良いでしょう。この 2 つ目のアラームが起動すると、アプリケーションは必要な API を呼び出し、結果を保存します。アプリケーションで毎分 0 秒に表示を更新したい場合は、再度 API を呼び出すのではなく、以前に保存した結果を使用します。このアプローチでは、APIを呼び出すタイミングが均等に分散されます。さらに、表示の更新時に、API 呼び出しによってレンダリングが遅延することもありません。

毎分 0 秒以外に、毎時 0 分、深夜 0 時など、よく使われる同期時刻もターゲットにしないよう注意してください。

レスポンスの処理

ウェブサービス リクエストに対する個々のレスポンス フォーマットの厳密性は保証されていません(一部の要素が欠落しているか、複数の場所にある可能性があります)。そのため、特定のレスポンスについて返されたフォーマットが、異なるクエリで同じになるという想定はしないでください。代わりに、レスポンスを処理し、で適切な値を選択してください。このセクションでは、これらの値をウェブサービス レスポンスから動的に抽出する方法について説明します。

Google Maps ウェブサービスでは、わかりやすいレスポンスを提供していますが、必ずしもユーザーにとってわかりやすいとはいえません。クエリを実行する場合は、一連のデータを表示するのではなく、いくつかの特定の値を抽出することになります。一般的には、ウェブサービスからのレスポンスを解析し、関心のある値のみを抽出します。

使用する解析スキームは、出力を XML または JSON のどちらで返すかによって異なります。既に JavaScript オブジェクトの形式である JSON レスポンスは、クライアント上の JavaScript 内で処理できます。XML レスポンスは、XML プロセッサと XML クエリ言語を使用して、XML 形式内の要素をアドレス指定するように処理する必要があります。次の例では XPath を使用しています。これを使用するのは、XML 処理ライブラリで一般的にサポートされているからです。

XPath による XML の処理

XML は比較的成熟した構造化情報形式であり、データ交換に使用されます。JSON ほど軽量ではありませんが、多くの言語をサポートし、堅牢なツールを提供しています。たとえば、XML を Java で処理するためのコードは、javax.xml パッケージに組み込まれています。

XML レスポンスを処理する場合は、XML ドキュメント内のノードを選択するための適切なクエリ言語を使用する必要があります。要素が XML マークアップ内の絶対位置にあるという想定はしないでください。XPath は、XML ドキュメント内のノードや要素を一意に記述するための言語構文です。XPath 式を使用すると、XML レスポンス ドキュメント内の特定のコンテンツを識別できます。

XPath 式

XPath について多少の知識を持っていると、堅牢な解析スキームの開発に大いに役立ちます。このセクションでは、複数の要素をアドレス指定したり複雑なクエリを作成したりできるように、XML ドキュメント内の要素を XPath でアドレス指定する方法に重点を置いて説明します。

XPath では、式を使用して XML ドキュメント内の要素を選択します。その構文は、ディレクトリ パスに使用される構文と似ています。これらの式は、DOM と似た階層ツリーである XML ドキュメント ツリー内の要素を特定します。通常、XPath 式は貪欲であり、これは、指定された条件を満たすすべてのノードに一致することを意味します。

例を示すために、次の抽象 XML を使用します。

<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>

式でのノード選択

XPath 選択では、ノードを選択します。ルートノードにドキュメント全体が含まれます。このノードを選択するには、特別な式「/」を使用します。ルートノードは、XML ドキュメントの最上位ノードではありません。実際には、この最上位要素の 1 つ上のレベルに存在し、その要素を含んでいます。

要素ノードは、XML ドキュメント ツリー内のさまざまな要素を表します。たとえば、<WebServiceResponse> 要素は、上記のサンプル サービスで返される最上位要素を表します。先頭に「/」文字を付けるか付けないかによって、個々のノードを絶対パスまたは相対パスで選択します。

  • 絶対パス: 「/WebServiceResponse/result」式では、<WebServiceResponse> ノードの子要素であるすべての <result> ノードを選択します(これらの要素は両方ともルートノード「/」の子孫です)。
  • 現在のコンテキストからの相対パス: 式「result」は、現在のコンテキスト内のすべての<result>要素に一致します。通常は 1 つの式でウェブサービスの結果を処理するため、一般的にはコンテキストを気にする必要はありません。

これらの式は、ダブル スラッシュ(//)で示されるワイルドカード パスを追加して拡張できます。このワイルドカードは、介在するパスにゼロ個以上の要素が一致する可能性があることを示します。たとえば、XPath 式「//formatted_address」は、現在のドキュメントでその名前を持つすべてのノードに一致します。式 //viewport//lat は、<viewport> を親要素として追跡できるすべての <lat> 要素に一致します。

デフォルトでは、XPath 式はすべての要素に一致します。述語を角カッコ([])で囲んで指定することによって、特定の要素に一致するように式を制限できます。たとえば、XPath 式「/GeocodeResponse/result[2]」は、常に 2 番目の result を返します。

式のタイプ
ルートノード
XPath 式: 「/
選択:
    <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>
    
絶対パス
XPath 式: 「/WebServiceResponse/result
選択:
    <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>
    
ワイルドカード付きパス
XPath 式: 「/WebServiceResponse//location
選択:
    <location>
     <lat>37.4217550</lat>
     <lng>-122.0846330</lng>
    </location>
    
述語付きパス
XPath 式: 「/WebServiceResponse/result[2]/message
選択:
    <message>The secret message</message>
    
最初の result のすべての直接の子要素
XPath 式: 「/WebServiceResponse/result[1]/*
選択:
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    
type テキストが「sample」である resultname
XPath 式: 「/WebServiceResponse/result[type/text()='sample']/name
選択:
    Sample XML
    

要素を選択する場合は、オブジェクト内のテキストだけでなくノードを選択することに注意することが重要です。通常は、一致するすべてのノードを反復処理してテキストを抽出する必要があります。テキストノードを直接照合することもできます。次のテキストノードをご覧ください。

XPath では、属性ノードもサポートされています。ただし、すべての Google マップ ウェブサービスは属性のない要素を提供しているため、属性の照合は必要ありません。

式でのテキスト選択

XML ドキュメント内のテキストは、XPath 式でテキストノード演算子を使用して指定します。この演算子「text()」は、指定したノードからのテキストの抽出を示します。たとえば、XPath 式「//formatted_address/text()」は、<formatted_address> 要素内のすべてのテキストを返します。

式のタイプ
すべてのテキストノード(空白を含む)
XPath 式: 「//text()
選択:
    sample
    Sample XML

    37.4217550
    -122.0846330
    The secret message
    
テキストの選択
XPath 式: 「/WebServiceRequest/result[2]/message/text()
選択:
    The secret message
    
コンテキスト依存の選択
XPath 式: 「/WebServiceRequest/result[type/text() = 'sample']/name/text()
選択:
    Sample XML
    

式を評価して一連のノードを返し、その「ノードセット」を反復処理して各ノードからテキストを抽出することもできます。次の例でこの方法を使用します。

XPath の詳細については、XPath W3C 仕様をご覧ください。

Java での XPath の評価

Java では、XML の解析および javax.xml.xpath.* パッケージ内の XPath 式の使用を幅広くサポートしています。そのため、このセクションのサンプルコードでは、Java を使用して、XML の使用方法や XML サービス レスポンスからのデータの解析方法を示します。

Java コードで XPath を使用するには、XPathFactory のインスタンスを作成し、そのファクトリー上で newXPath() を呼び出して XPath オブジェクトを作成します。その後、このオブジェクトは evaluate() メソッドを使用して、渡された XML と XPath 式を処理できます。

XPath 式を評価する場合は、返される可能性があるすべての「ノードセット」を反復処理する必要があります。これらの結果は Java コードで DOM ノードとして返されるため、NodeList オブジェクト内のこのような複数の値を取得し、そのオブジェクトを反復処理してノードからテキストまたは値を抽出する必要があります。

次のコードは、XPath オブジェクトを作成して XML と XPath 式を割り当て、その式を評価して関連するコンテンツを出力する方法を示します。

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");
    }
  }
}

js-v2-samples からコードをダウンロードしてください

JavaScript による JSON の処理

JSON(Javascript Object Notation)は、レスポンスが軽量であるという点では、明らかに XML よりも優れています。形式が既に有効な JavaScript オブジェクトであるため、JavaScript での結果の解析が簡単になります。たとえば、JSON 結果オブジェクト内の 'formatted_address' キーの値を抽出するには、次のコードを使用してそのキーにアクセスするだけです。

for (i = 0; i < myJSONResult.results.length; i++) {
  myAddress[i] = myJSONResult.results[i].formatted_address;
}

JSON には複数の値が含まれている可能性があるため、すべての有効な値を取得する場合は、results 配列の端から端まで反復処理することが最も賢明です。ただし、実際には、最初の結果(results[0])のみを返す必要がある場合があります。

他の言語で JSON を解析するのは、それほど難しくはありません。次の Python の例は、ジオコーディング ウェブサービスのリクエストを開始し、配列内の結果の formatted_address 値をすべてユーザーに表示します。

import simplejson, urllib

GEOCODE_BASE_URL = 'https://maps.googleapis.com/maps/api/geocode/json'

def geocode(address, **geo_args):
    geo_args.update({
        'address': address
    })

    url = GEOCODE_BASE_URL + '?' + urllib.urlencode(geo_args)
    result = simplejson.load(urllib.urlopen(url))

    print simplejson.dumps([s['formatted_address'] for s in result['results']], indent=2)

if __name__ == '__main__':
    geocode(address="San+Francisco")

Output: [ "San Francisco, CA, USA" ]

js-v2-samples からコードをダウンロードしてください

sensor パラメータ

Google Maps API では、以前はユーザーの位置情報の検出にアプリケーションでセンサーを使用するかどうかを示すため sensor パラメータを含める必要がありましたが、このパラメータは必要なくなりました。

フィードバックを送信...

Google マップ ウェブサービス API
Google マップ ウェブサービス API