前幾天談了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結果傳遞需求的啦! (筆記)


Comments

# by chester

Great!非常感謝!學習了!

# by Ammon

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

# by ray

你好,我是初學的,我試將以上的copy在網頁上,但失敗, 請問可否寄給我完整的demo檔案?謝謝 ray453523@gmail.com

# by Jeffrey

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

# by Liou

我複製了你的展示上的程式碼 可是我執行後卻說 36行29字元 必須要有識別項!! 怎麼會這樣?

# by Jeffrey

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

# by Wolf

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

# by 謝尚明

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

# by Jeffrey

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

# by 大鼻子

您好 最近在找尋座標轉為門牌地址的方式,並在網上看到您寫的這篇文章。想請問一下,不知您近期您是否還有使用過MAP API相關服務?是否現在使用此類功能均須要先申請API KEY的權限呢?如果說我想要批次將手上座標轉為門牌地址是否相關功能呢!!有看過您提供的這個範例,但是否無法進行批次處理呢 謝謝您 耐心看完我的提問

# by Jeffrey

to 大鼻子, 我很久沒接觸地圖應用了,依我所知 Goggle MAP API 這些年大幅改版過,對於新版 API 我沒啥經驗,恐幫不上忙。

Post a comment