答客問-使用google.maps.Geocoder()轉換地址

前幾天談了Google Maps API地址轉換,Ammon提醒其實Google Maps API也有提供從Javascript端進行地理編碼的做法,可以全部在Client端處理完成,不需動用ashx。另外,chester希望能有使用純Javascript進行地址轉換的範例,所以,範例來了!

本次的程式幾乎跟上次完全相同,只改寫了geocodeAjax()函數而已。原本的做法是呼叫ashx,這次則改用goole.maps.Geocoder(),透過Geocoder.geocode(request, callbackFunction)進行地理編碼。由於是透過callbackFunction以非同步方式傳回結果,因此要仿效$.ajax()傳回Deferred.promise()以實現全部結果都傳回後才開始繪製地圖的效果。

另外,實測時發現Geocoder()似乎會管控使用量,如果不間斷地連續呼叫一陣子後便會停擺,為此我加入setTimeout模擬延遲,讓每次查詢時間間隔一秒,最後才順利把44筆都查完。

程式碼如下: (其實只要看geocodeAjax()就好,其他的沒改)

<!DOCTYPE html>
<html>
<head runat="server">
    <title>Geocoding Test</title>
    <script type='text/javascript' 
            src='http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.js'></script>   
    <script src="https://maps.google.com/maps/api/js?sensor=true"></script>
    <script src="DynaMarkerIcon.js" type="text/javascript"></script>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
    <style>
        body,input { font-size: 9pt; }
        html { height: 100% }  
        body { height: 100%; margin: 0px; padding: 0px }  
        #map_canvas { height: 100% }        
    </style>
    <script>
        $(function () {
            var markers = [];
            geocoder = new google.maps.Geocoder();
            var c = 0;
            //透過Google Geocoder將地址轉為經緯座標
            function geocodeAjax(name, addr) {
                //利用Deferred物件協助非同步呼叫全部完成的時機
                var def = new jQuery.Deferred();
                //geocoder似乎有使用量管控,若快速連續呼叫會停止運作
                //在此使用setTimeout節流,每次查詢間隔一秒鐘
                setTimeout(function() {
                    //呼叫decode(),傳入參數及Callback函數
                    geocoder.geocode({ address: addr }, function (results, status) {
                        //檢查執行結果
                        if (status == google.maps.GeocoderStatus.OK) {
                            var loc = results[0].geometry.location;
                            markers.push({
                                title: name + "@" + addr,
                                latlng: new google.maps.LatLng(loc.lat(), loc.lng())
                            });
                            //呼叫Deferred.resolve(),表示執行成功
                            def.resolve();
                        }
                        else
                        {
                            //呼叫Deferred.reject(),表示執行失敗
                            def.reject();
                        }
                    });
                }, c++ * 1000);
                //傳回Promise物件,以協調非同步呼叫結果
                return def.promise();
            }
            $.get("AddrList.txt", {}, function (list) {
                var lines = list.replace(/\r/g, "").split('\n');
                var deferredArray = [];
                //lines[i]格式如下:
                //中正中隊-華山分隊,(02)23412668,中正區北平東路1號 
                for (var i = 0; i < lines.length; i++) {
                    var parts = lines[i].split(',');
                    //以AJAX進行地址轉換,並將$.ajax() deferred物件放入陣列中
                    deferredArray.push(geocodeAjax(parts[0], parts[2], lines[i]));
                }
                //利用Deferred特性,在所有地址轉換呼叫完畢後,繪製地圖
                $.when.apply(null, deferredArray).then(function () {
                    //設定地圖參數
                    var mapOptions = {
                        mapTypeId: google.maps.MapTypeId.ROADMAP //正常2D道路模式
                    };
                    //在指定DOM元素中嵌入地圖
                    var map = new google.maps.Map(
                        document.getElementById("map_canvas"), mapOptions);
                    //使用LatLngBounds統計檢視範圍
                    var bounds = new google.maps.LatLngBounds();
                    //加入標示點(Marker)
                    for (var i = 0; i < markers.length; i++) {
                        var m = markers[i];
                        //將此座標納入檢視範圍
                        bounds.extend(m.latlng);
                        var marker = new google.maps.Marker({
                            position: m.latlng,
                            title: m.title,
                            map: map
                        });
                    }
                    //調整檢視範圍
                    map.fitBounds(bounds);
                });
            });
        });
    </script>
</head>
<body>
<div id="map_canvas" style="width:100%; height:100%"></div>
</body>
</html>

執行結果如下圖,還順便用HttpWatch偷窺了查詢過程的網路封包,發現Geocoder()背後在呼叫maps/api/js/GeocodeService.Search?…進行查詢,而傳回結果中的"_xdc_._fw7f14 && _xdc_._wf7f14({ … })"明白地告訴我們,Google也是用JSONP處理跨網域API結果傳遞需求的啦! (筆記)

歡迎推文分享:
Published 21 June 2012 07:59 AM 由 Jeffrey
Filed under:
Views: 40,642



意見

# chester said on 20 June, 2012 11:16 PM

Great!非常感謝!學習了!

# Ammon said on 21 June, 2012 05:07 AM

之前為了某案上千筆資料查詢寫了個批次處理,放到 jsFiddler 上供大家使用。 http://goo.gl/O6fxW

# ray said on 07 October, 2012 03:02 PM

你好,我是初學的,我試將以上的copy在網頁上,但失敗,

請問可否寄給我完整的demo檔案?謝謝

ray453523@gmail.com

# Jeffrey said on 08 October, 2012 09:33 PM

to ray, 我放了一個線上展示(www.darkthread.net/.../lab5.htm),因為每一秒只查一筆的闗係(加上我偷懶沒寫查詢進度顯示),要有點耐心等幾十秒結果才會出現。程式用到的DynaMarkerIcon.js跟AddrList.txt可以由該網址下載。

# Liou said on 31 October, 2012 01:39 AM

我複製了你的展示上的程式碼

可是我執行後卻說

36行29字元

必須要有識別項!!

怎麼會這樣?

# Jeffrey said on 31 October, 2012 04:51 AM

to Liou, 原來的程式寫法在IE7執行有問題,請將"latlng: new google.maps.LatLng(loc.lat(), loc.lng()), "最後方的","刪除,應可解決問題。本文的程式碼已修改,感謝回饋。

# Wolf said on 23 May, 2014 03:40 AM

謝謝大大的文章~ 受益良多!

# 謝尚明 said on 07 June, 2015 03:42 AM

想學習能自動取得經緯度後將以圖面及中文地址呈現,能否教導指點

# Jeffrey said on 07 June, 2015 08:19 PM

to 謝尚明, 我找到一個呈現中文地址的範例:kuro.tw/.../address-information-is-obtained-through-google-map-geocoder-with-the-latitude-and-longitude

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<June 2012>
SunMonTueWedThuFriSat
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567
 
RSS
創用 CC 授權條款
【廣告】
twMVC
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication