Webフォーム 地図から住所入力

スマートフォンやタプレットなどの携帯端末を使う機会が増えているが、ガラケーに比べりゃ簡単になったとはいえ、画面を触りまくる文字入力は面倒というか上手くできなくてイライラ。できるだけ文字入力はしたくない。じゃあ、携帯端末で文字入力しなきゃならないのって何だっけ?と思い返したらウェブサイトのフォームってことが多い気がする。(「がとらぼ」の中の人の使用状況では)
中でも住所入力は面倒かな。
最近はこの手の情報の入力はオートコンプリート機能も使える場合もあるが、フォームによっては対応してなかったり酷いのになると正しい項目に入らなかったりで余計にイラッ。 そこで、できるだけ文字入力しなくてすむように、地図で自分の家をクリックして住所入力するフォームの非常に簡単なのを作ってみた。

この記事はウェブサイトの運営者向けなので念のため。

注意点
Google Maps APIを使うのでAPIのキーが必要。
HTTPSのページ(サイト)でないと使えない。(Google Maps APIの仕様)
下の例は簡単にするため日本の一般的な住所の形式にのみ対応
手抜きなのでページを開いた際にいきなり現在地取得の許可を求められる方式
地図から住所入力

DEMOページ

使い方

  1. フォームのページを開いたら現在地を取得しようとする。
  2. 現在地が取得できない環境なら日本橋の地図とマーカーが表示される。5へ。
  3. 所得した現在地が地図上のマーカーで表示され、フォームにその地点の住所が入力される。
  4. マーカーの位置が自宅であるなら8へ。
  5. 位置が違うなら地図を操作して正しい自宅などをクリック(タッチ)。
  6. 地図をクリックした場所にマーカーが表示され、フォームにその地点の住所が入力される。
  7. マーカーの位置が自宅でないなら5に戻る。正しければフォームに入力された住所を確認
  8. フォームの住所が正しいなら入力完了
  9. フォームの住所が正しくないなら修正(これは手入力するしかない)

つまり、「現在地」が自宅ならラッキー、そうでないなら地図で自宅をクリック。フォームに表示された住所が正しければ良かったね。正しくなければ、あきらめて自分で直せ。こんな感じ。

HTMLのソース
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Cache-Control" content="no-cache">
    <meta http-equiv="Expires" content="0">
    <title>悪いインターネット | フォームで地図から住所入力</title>
</head>
<body>
    <h1>フォームで地図から住所入力</h1>

    <ul>
        <li>緯度: <span id="lat"></span></li>
        <li>経度: <span id="lng"></span></li>
        <li>住所: <span id="address"></span></li>
    </ul>
    <div style="width:calc(100% - 20px); height:450px; margin:10px;" id="map"></div>

    <p>上の地図でクリックした地点の住所が下に入力されます。<br />
    その後、必要に応じて修正して下さい。</p>

    <form action="./hoge.php" method="post">
        <p>郵便番号: <input id="postal" type="text" name="postal-code" size="8"></p>
        <p>都道府県: <input id="addr1" type="text" name="address-level1" size="8"></p>
        <p>市区町村: <input id="addr2" type="text" name="address-level2" size="24"></p>
        <p>番地・建物名 (1行目): <input id="addr3" type="text" name="address-line1" size="48"></p>
        <p>番地・建物名 (2行目): <input id="addr4" type="text" name="address-line2" size="48"></p>
        <p><input type="submit" value="送信"><input type="reset" value="リセット"></p>
    </form>
    <p>↑の送信ボタンはダミーです。</p>

    <script src="//maps.google.com/maps/api/js?key=自分のウェブサイトで使用できるAPIキー"></script>
    <script src="./map.js"></script>
</body>
</html>
javascriptのソース  (上のHTMLでは最後の方のmap.js)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//グローバル変数(東京都中央区日本橋の座標:地図の初期座標)
lng = 139.77450370788574;    
lat = 35.68421096423828;    

// GeoLocation APIに対応している場合
if( navigator.geolocation ){
    //現在位置取得
    navigator.geolocation.getCurrentPosition(getPosition);
}

function getPosition(position){
    lat = position.coords.latitude;
    lng =  position.coords.longitude;
}

google.maps.event.addDomListener(window, 'load', function() {
    var objMarker;
    var objMap;
    var latlng = new google.maps.LatLng(lat, lng);

    var optMap = { 
        zoom: 16,
        center: latlng,
        mapTypeId: google.maps.MapTypeId.ROADMAP,
        scaleControl: true
    };
    objMap = new google.maps.Map(document.getElementById('map'), optMap);

    //初期位置の住所取得
    getAddress(latlng);

    //マーカー
    objMarker = new google.maps.Marker({ 
        position: latlng,
        map: objMap
    });

    // マップクリックイベントを追加 
    google.maps.event.addListener(objMap, 'click', function(e) { 
        // ポジションを変更 
        objMarker.setPosition(e.latLng);
        // 地図の中心をマーカーの位置に変更 
        //objMap.setCenter(e.latLng);
        objMap.panTo(e.latLng);
        // 住所取得
        getAddress(e.latLng);
    });
});

function getAddress(position){
    var geocoder = new google.maps.Geocoder();
    var postalcode = "";
    var prefecture = "";
    var locality = "";
    var level1 = "";
    var level2 = "";
    var level3 = "";
    var level4 = "";
    var level5 = "";
    var premise = "";
    var subpremise = "";
    var street = "";

    //緯度/経度書き出し
    document.getElementById( 'lat' ).innerHTML = position.lat() ;
    document.getElementById( 'lng' ).innerHTML = position.lng() ;

    geocoder.geocode({'latLng': position, 'language': 'ja'}, function(results, status) {
        if(status==google.maps.GeocoderStatus.OK){
            if (results[1]) {
                //郵便番号+住所書き出し
                document.getElementById( 'address' ).innerHTML = results[0].formatted_address.replace(/^日本, /, '');

                for(var i=0;i < results[0].address_components.length; i++){
                    for(var j=0; j < results[0].address_components[i].types.length; j++){
                        if(results[0].address_components[i].types[j] == "postal_code"){
                            postalcode = results[0].address_components[i].short_name;
                        }
                        if(results[0].address_components[i].types[j] == "administrative_area_level_1"){
                            prefecture = results[0].address_components[i].short_name;
                        }

                        //2018年より郡がadministrative_area_level_2、市区町村がlocality
                        if(results[0].address_components[i].types[j] == "administrative_area_level_2"){
                            locality = results[0].address_components[i].short_name;
                        }
                        if(results[0].address_components[i].types[j] == "locality"){
                            locality = locality + results[0].address_components[i].short_name;
                        }

                        if(results[0].address_components[i].types[j] == "sublocality_level_1"){
                            level1 = results[0].address_components[i].short_name;
                        }
                        if(results[0].address_components[i].types[j] == "sublocality_level_2"){
                            level2 = results[0].address_components[i].short_name;
                        }
                        if(results[0].address_components[i].types[j] == "sublocality_level_3"){
                            level3 = results[0].address_components[i].short_name;
                        }
                        if(results[0].address_components[i].types[j] == "sublocality_level_4"){
                            level4 = results[0].address_components[i].short_name;
                        }

                        if(results[0].address_components[i].types[j] == "premise"){
                            premise = results[0].address_components[i].short_name;
                        }
                        if(results[0].address_components[i].types[j] == "subpremise"){
                            subpremise = results[0].address_components[i].short_name;
                        }
                    }
                }

                //番地を繋ぐハイフン
                if (isAllNumber(level3) & level3 !== "" && isAllNumber(level4) && level4 !== ""){
                    level4 = "−" + level4;
                }

                street = level3 + level4;

                //地方などで「番地1」が空で「番地2」が存在する場合は番地2を番地1に付け替える
                if (street == "" && premise !== ""){
                    street = premise;
                    premise = "";
                }

                //住所2(premise)が番地の数字だけの場合は「番地1」に追記する形にする
                if (isAllNumber(premise) && premise !== ""){
                    street = street + "−" + premise;
                    premise = ""; 
                }

                //建物名があればpremiseに追加
                premise = premise + " " + subpremise;

                //この下のフォーム住所書き出し innerHTMLではなくvalueなので注意
                document.getElementById( 'postal' ).value = postalcode;
                document.getElementById( 'addr1' ).value = prefecture;
                document.getElementById( 'addr2' ).value = locality + " " + level1 + level2;
                document.getElementById( 'addr3' ).value = street;
                document.getElementById( 'addr4' ).value = premise;
            } else {
                document.getElementById( 'address' ).innerHTML = '取得できませんでした';
            }
        } else {
            document.getElementById( 'address' ).innerHTML = 'エラー: 取得できませんでした';
        }
    });
}


function isAllNumber(val){
    var ptn = /^[0-90-9]*$/;
    return ptn.test(val);
}

2020年10月27日修正:
マーカーが正しく移動しなくなっていたので修正しました。合わせて、地図の瞬間移動をパン(移動するアニメーション)に変更しています。
また、2018年のGoogle Maps側の仕様変更で表示される項目と値が盛大にズレるようになっていたのでその辺りの処理を大幅に改変した。

フォームの「送信」ボタンを押した時に実行されるスクリプト(上のソースではhoge.php)についてはこの記事では関係ないので省略。

住所を全部求める方は凄く簡単だけど、都道府県や市町村、番地など個別に取るのはjavascriptだと意外と大変。グルングルン回して取るという方法。ここは気に入らないので何か良い方法ないかしら。

API使ってるとはいえ、こんな簡単にできていいのだろうか。

ところで、世の中には地図が苦手な人も結構多いんだよね・・ボソッ