郵便番号入力も不要! Google Mapを使ってクリックだけで住所入力欄へ住所を自動反映させる

![NewMapsLogo_GPlus%28padded%292.png](https://qiita-image-store.s3.amazonaws.com/0/105193/502f52d6-8375-0a3e-4e7a-def1bbeca38a.png)
郵便番号がわからなくてもOK。初期入力の必要のない住所自動入力機能を考えてみたよｗ
気になる方は、下のサンプルで試してみてください。
▼以下サンプル画面
http://syany.github.io/FindAddressSample.html

次にやり方をいくつか紹介します。まずは、何も入力しないで「マップから住所情報を検索する」ボタンを押すメイン機能から
# (おためし1)マップから住所入力する
![image001.png](https://qiita-image-store.s3.amazonaws.com/0/105193/b4137227-e88a-932c-f6d8-dff1ddfd288c.png)
初期画面からすぐに郵便番号欄横の「マップから住所を検索する」ボタンを押します。すると以下のような子画面が、、、
![image003.png](https://qiita-image-store.s3.amazonaws.com/0/105193/834e4729-b2bd-1c47-0b3c-7784dd01b791.png)
表示されるので、表示されたGoogle Mapをドラックしたり拡大したりしながら、入力したい住所付近をクリックします。
ここではサンプルとして六本木ヒルズのノースタワーを選択してみました。
![image005.png](https://qiita-image-store.s3.amazonaws.com/0/105193/accdfb72-e6d3-80f8-857e-0f020d6070fc.png)
すると、クリック箇所の住所を示す吹き出しが表示されるので、問題なければ「OK」ボタンをクリックします。
![image013.png](https://qiita-image-store.s3.amazonaws.com/0/105193/b806e449-80cf-3aff-f406-cbebec5121dc.png)
反映されました。
## メリット
郵便番号では入力補完が難しい建物情報まで入力されること。これにより例えば、アパートやマンションに住んでいる方も残りは号室を入力するくらいで住所入力が完了するのではないでしょうか？

# (おためし2)キーワードや住所の一部を入力後ボタン押す
以降はGoogle Geocoder/ Map APIを使用したならではの機能です。次のように、「虎ノ門ヒルズ」とだけ入力し、「マップから住所情報を検索する」ボタンを押します。
![image015.png](https://qiita-image-store.s3.amazonaws.com/0/105193/f2ef330b-f71d-60e3-4cbe-3d73b3dd9241.png)

Google Geocoderに登録されている情報であるならば、そのキーワードだけで住所情報が表示されます。以下が子画面表示結果です。
![image017.png](https://qiita-image-store.s3.amazonaws.com/0/105193/87023097-9b5a-a0f3-faa3-16ff332f8182.png)

おためし１同様に、ここで「OK」ボタンをクリックすると
![image020.gif](https://qiita-image-store.s3.amazonaws.com/0/105193/e5bac126-e13b-24dc-afae-23c102ca0265.gif)

# (おためし3)郵便番号入力後ボタン押す
１ヶ月の画像アップロードのキャパを超えてしまったので、文字だけの説明となりますが、おためし２同様にキーワード検索として、郵便番号を入力すると郵便番号で検索できる最も近いマップが表示されます。
そこから、より設定したい場所へ移動し、吹き出しの「OK」をクリックすることで、今までの機能の通り郵便番号からも住所情報を設定できます。

# アーキテクチャ
親画面は、子画面を出すためのwindow.openメソッドだけですので
子画面のメインアーキテクチャ部分のコードを公開します。
（使用するにはGoogle App のIDとScript、jQueryが必要です。

```JavaScript:sample.js
  $(function() {

    var marker = null,
    resultAddress = null,
    pw = window.opener,
    mapObj = new google.maps.Map(
        document.getElementById('UraAddressMap'), {
          zoom: 7,
          center: new google.maps.LatLng(36, 139),
          mapTypeId: google.maps.MapTypeId.ROADMAP
        });
    // 親ウィンドウが閉じている場合はすぐ閉じる
    if (!pw || pw.closed) {
      winClose();
    }
    // 親ウィンドウにjQueryがない場合、すぐ閉じる
    if (!window.opener.jQuery) {
      console.debug('jQuery not used in parent window.');
      winClose();
    }

    var $postalcode = pw.jQuery('#postalcode'),
    $prefecture = pw.jQuery('#prefecture'),
    $cities = pw.jQuery('#cities'),
    $buildings = pw.jQuery('#buildings');

    /**
     * Google Geocodeの結果用メソッド。<br>
     * 正常な戻り値があれば、マーカーを再セットし、OKボタン付き吹き出しを表示する。
     */
    function resultGeocode(results, status) {
      if (status == google.maps.GeocoderStatus.OK) {
        if (results[0].geometry) {
          // マーカーのリセット
          if (marker) {
            marker.setMap(null);
          }
          // マーカーの再設定
          resultAddress = results[0];
          marker = new google.maps.Marker({
            position: resultAddress.geometry.location,
            map: mapObj
          });
          // 吹き出しの表示
          new google.maps.InfoWindow({
            content: '[' + resultAddress.formatted_address + ']を使用しますか？<br />'+
             '<span style="color:gray;">(Lat, Lng) = ' + resultAddress.geometry.location + '</span><br />'+
             '<input type="button" id="uraAddressOK" value="OK" onClick="uraCallback();" />'
          }).open(mapObj, marker);
        }
      } else if (status == google.maps.GeocoderStatus.INVALID_REQUEST) {
        alert("リクエストエラー。geocode()向けGeocoderRequestを確認してください");
      } else if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
        alert("一定時間内に送るリクエスト数の限界エラー");
      } else if (status == google.maps.GeocoderStatus.REQUEST_DENIED) {
        alert("このページではジオコーダの利用が許可されていません");
      } else if (status == google.maps.GeocoderStatus.UNKNOWN_ERROR) {
        alert("サーバエラー");
      } else if (status == google.maps.GeocoderStatus.ZERO_RESULTS) {
        alert("ゼロエラー");
      } else if (status == google.maps.GeocoderStatus.ERROR) {
        alert("サーバ通信時のエラー");
      } else {
        alert("不明");
      }
    }
    /**
     * 緯度経度からGeocoder→Mapへ反映を行う。
     */
    function getGeocodeLatLng(latLng) {
      var geocoderObj = new google.maps.Geocoder();
      geocoderObj.geocode({
        latLng: latLng
      }, resultGeocode);
    }
    /**
     * 住所（キーワード）からGeocoder→Mapへ反映を行う。
     */
    function getGeocodeAddress(address) {
      var geocoderObj = new google.maps.Geocoder();
      geocoderObj.geocode({
        address: address
      }, resultGeocode);
    }
    /**
     * Mapの対象箇所クリック→Geocoder→Mapへ反映を行う。
     */
    google.maps.event.addListener(mapObj, 'click', function(mouseEvent) {
      getGeocodeLatLng(mouseEvent.latLng);
    });
    /**
     * Map上に出力された吹き出しのOK押下に親ウィンドウ内フォームの反映
     */
    window.uraCallback = function(){

      // 親ウィンドウが閉じていればすぐ閉じる
      if (!pw || pw.closed) {
        winClose();
      }

      // 一度文字列をリセット
      $postalcode.val('');
      $prefecture.val('');
      $cities.val('');
      $buildings.val('');
      // タイプを条件に各フォームに値を入れてく
      for(var address,oldSublocality, idx = resultAddress.address_components.length - 1;
        address = resultAddress.address_components[idx]; idx--) {
        var type = address.types[0];
        // "postal_code":郵便番号
        // "administrative_area_level_1": 都道府県
        // "locality": 市町村
        // "political":丁目,("sublocality_level_4-6":号)
        // "premise"：ビル
        if (type === 'locality') {
          $cities.val($cities.val() + address.long_name);
        } else if (type === 'political') {
          var sublocality = address.types[address.types.length - 1];
          if ((sublocality === 'sublocality_level_4' && oldSublocality === 'sublocality_level_3') ||
              (sublocality === 'sublocality_level_5' && oldSublocality === 'sublocality_level_4') ||
              (sublocality === 'sublocality_level_6' && oldSublocality === 'sublocality_level_5')) {
            // 番地の後号が続く場合、数値がつながらないようにする（２－２が２２とならないよう）
            $cities.val($cities.val() + '－' + address.long_name);
          } else {
            $cities.val($cities.val() + address.long_name);
          }
          oldSublocality = sublocality;
        } else if (type === 'administrative_area_level_1') {
          $prefecture.val( $prefecture.val() + address.long_name);
        } else if (type === 'postal_code') {
          $postalcode.val($postalcode.val() + address.long_name);
        } else if (type === 'premise') {
          $buildings.val($buildings.val() + address.long_name);
        }
      }
      // 必要ならばここで閉じる
      winClose();
    };

    // 初期設定。住所情報が存在する場合、検索を開始する。
    var addressText = $postalcode.val();
    addressText += (addressText) ? ' ' + $prefecture.val() : $prefecture.val();
    addressText += (addressText) ? ' ' + $cities.val() : $cities.val();
    addressText += (addressText) ? ' ' + $buildings.val() : $buildings.val();
    if (addressText) {
      mapObj.setZoom(17);
      getGeocodeAddress(addressText);
    }
  });
```

(ソースは https://github.com/syany/UraAddress からDL可能です。(Eclipseの静的WebプロジェクトでHTTPプレビューサーバから確認することができます。))

# 経緯
カートや会員登録で、住所自動入力機能は当たり前のように見られるようになりましたが、郵便番号を入力しなければならないものや、住所を入力して郵便番号を補完するようなしくみがほとんどでした。

Geocoderが緯度経度から住所情報を返却するしくみがあることを知り、この無入力での自動住所入力機能を考えてみました。

- 引っ越したばかりの人
- 郵便番号すら覚えたくない
- 入力が苦手

の方におすすめです。

Google Apps Script になってから制約も多くなりましたので
注意をしながら、色々試していきましょう。