[ASP.net WebForm/Google] 解決Google API地理編碼技術地址轉JSON要求上限10筆問題

[ASP.net WebForm/Google] 解決Google API地理編碼技術地址轉JSON要求上限10筆問題

若照著之前發的幾篇文章:

[ASP.net WebForm/Google] 在Google Map上放置多個標記地點(Marker)/API 3版

[ASP.net WebForm/Google] Google Map標記點(Marker)更改圖示、加入對話框/API 3版

 

在地圖上標記Marker會發現資料量一多,或一次超過10筆的話

程式會出錯

image

因為之前範例都提搭配Google API地理編碼技術(地址轉JSON)來標記Marker

Google API地理編碼技術一次要求太頻繁的話,API會發生OVER_QUERY_LIMIT錯誤

image

趁著今天有空,想了一個使用者體驗較好的解決方式,每使用一次地理編碼技術就延遲300毫秒再繼續處理下一筆

不是在Server端Thread.Sleep(300);喔,這樣使用者會等很久才看到畫面。

 

直接看原始碼吧

新增一個DataTableSource.ashx


<%@ WebHandler Language="C#" Class="DataTableSource" %>

using System;
using System.Web;
/*要引用以下的命名空間*/
using System.Data;
using System.Data.SqlClient;
using System.Net;
using System.IO;
/*Json.NET相關的命名空間*/
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public class DataTableSource : IHttpHandler
{
    
    public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "text/plain";
        //取得DataTable
        DataTable dt = this.queryDataTable();
        //將DataTable轉成JSON字串
        string str_json = JsonConvert.SerializeObject(dt, Formatting.Indented);
        context.Response.Write(str_json);
    }
 
    /// <summary>
    /// 從DB撈出DataTable
    /// </summary>
    /// <returns></returns>
     private DataTable queryDataTable()
    {
         
        DataTable dt = new DataTable();
        dt.Columns.Add(new DataColumn("地址",typeof(string)));
        dt.Columns.Add(new DataColumn("名稱",typeof(string)));
        dt.Rows.Add("台北市羅斯福路2段100號", "飛碟電台大樓");
        dt.Rows.Add("台北市和平東路一段162號", "台灣師範大學行政部");
        dt.Rows.Add("台北市和平東路一段162號", "台灣師範大學行政部");
        dt.Rows.Add("台北市和平東路一段162號", "台灣師範大學行政部");
        dt.Rows.Add("台北市中正區羅斯福路2段164-1號", "捷運古亭站");
        dt.Rows.Add("台北市南京東路3段256巷20弄1號","敦化國中");
        dt.Rows.Add("台北市南京東路3段256巷20弄1號", "敦化國中");
        dt.Rows.Add("台北市南京東路3段256巷20弄1號", "敦化國中");
        dt.Rows.Add("台北市南京東路3段256巷20弄1號", "敦化國中");
        dt.Rows.Add("台北市南京東路3段256巷20弄1號", "敦化國中");
        dt.Rows.Add("台北市南京東路3段256巷20弄1號", "敦化國中");
        dt.Rows.Add("台北市南京東路3段256巷20弄1號", "敦化國中");
        dt.Rows.Add("台北市南京東路3段256巷20弄1號", "敦化國中");
        dt.Rows.Add("台北市南京東路3段256巷20弄1號", "敦化國中");
        dt.Rows.Add("台北市南京東路3段256巷20弄1號", "敦化國中");
        dt.Rows.Add("台北市南京東路3段256巷20弄1號", "敦化國中");
        dt.Rows.Add("台北市南京東路3段256巷20弄1號", "敦化國中");
        dt.Rows.Add("台北市南京東路3段256巷20弄1號", "敦化國中");
        dt.Rows.Add("台北市南京東路3段256巷20弄1號", "敦化國中");
        return dt;
    }


    
  
    
    
    public bool IsReusable {
        get {
            return false;
        }
    }

}

新增一個MarkerOne.ashx


<%@ WebHandler Language="C#" Class="MarkerOne" %>

using System;
using System.Web;
using System.IO;
using System.Net;
using NLog;
/*引用*/
using System.Configuration;

public class MarkerOne : IHttpHandler
{
    
    public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "text/plain";
        string address = context.Request.QueryString["address"];//中文地址
        context.Response.Write(this.convertAddressToJSONString(address));//輸出json字串
    }
    //把地址轉成JSON格式,這樣資訊裡才有緯經度
    //因為使用到地理編碼技術,請注意使用限制:http://code.google.com/intl/zh-TW/apis/maps/documentation/geocoding/#Limits
    private string convertAddressToJSONString(string address)
    {
     
        //string url = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=" + HttpContext.Current.Server.UrlEncode(address);
        //2017-10-12追記
        //最好申請API金鑰,要求服務提供的配額會多很多   
          string GoogleAPIKey = ConfigurationManager.AppSettings["GoogleAPIKey"];
          string url = "https://maps.googleapis.com/maps/api/geocode/json?address="+ HttpUtility.UrlEncode(address, Encoding.UTF8)+"&key=" + GoogleAPIKey;


        string result = String.Empty;
        System.Net.HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
        using (var response = request.GetResponse())
        using (StreamReader sr = new System.IO.StreamReader(response.GetResponseStream()))
        {
            result = sr.ReadToEnd();
        }
        
        return result;

    }
 
    public bool IsReusable {
        get {
            return false;
        }
    }

}

然後.aspx網頁的Code


<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <script src="Scripts/jquery-1.9.0.min.js"></script>
     <!--玩Google Map 一定要引用此js-->
    <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
</head>
<body>
    <form id="form1" runat="server">
     <!--地圖-->
     <div id="div_showMap" style="width: 800px; height: 600px">
    </div>

    </form>

    <script type="text/javascript">
        $(document).ready(init);
        //網頁上所有的DOM都載入後
        function init() {
            QueryDataTable();
        }
        //放DataTable資料的全域變數
        var array = new Array();
        //抓出地址資料
        function QueryDataTable() {
            $.ajax(
            {
                url: 'DataTableSource.ashx',
                type: 'post',
                async: true,
                data: {},
                dataType: 'json',
                success: function (datas) {
                    $(datas).each(function (index, item) {
                        array.push(item);//一一加入陣列
                    });
                    repeatFunc(0);//一個一個項目標記在地圖上

                } 
            });
        }
        var t;
        function repeatFunc(startIndex) {

            MarkerOne(array[startIndex]);//標記一筆在地圖上
            startIndex++;
            if (startIndex >= array.length) {
                clearTimeout(t);//停止遞迴
                return;//停止函數
            }
            t = setTimeout(function () {
                repeatFunc(startIndex);
            }, 300);//延遲300毫秒才處理下一筆
            
        }

        var first = true;
        var map;
        //把地址轉成JSON格式並在地圖上加入標記點
        function MarkerOne(item) {
            $.ajax({
                url: "MarkerOne.ashx",
                type: "get",
                data: { address: item.地址 },
                async: true,
                dataType: "json",
                success: function (r) {
                    var location = r.results[0].geometry.location;//取得此資料的位置
                    //建立緯經度座標物件
                    var latlng = new google.maps.LatLng(location.lat, location.lng);
                    if (first) {//第一次執行 
                        /*以哪個緯經度中心來產生地圖*/
                        var myOptions = {
                            zoom: 14,
                            center: latlng,
                            mapTypeId: google.maps.MapTypeId.ROADMAP
                        };
                        /*產生地圖*/
                        map = new google.maps.Map($("#div_showMap")[0], myOptions);
                        first = false;
                    } //End if (first == true) 

                    //加一個Marker到map中
                    var marker = new google.maps.Marker({
                        position: latlng,
                        map: map,
                        title: item.名稱
                    });
                },
                error: function (result) {
                    alert(result.responseText);
                }

            });
        }

    </script>
</body>
</html>

 

不過就我自己開發地理資訊系統,資料都是在進DB時就已輸入經度緯度,現在也不太用地理編碼技術了

 

2014.2.21追記:

黑暗執行緒網友寫了一篇Google Map API綜合技術應用,在工作專案上滿常見,可以參考看看:利用行動裝置GPS定位尋找臨近地點

2017.10.12追記

如果沒使用API金鑰就呼叫Google API服務的話,很容易出現以下錯誤訊息

{
   "error_message" : "You have exceeded your daily request quota for this API. We recommend registering for a key at the Google Developers Console: https://console.developers.google.com/apis/credentials?project=_",
   "results" : [],
   "status" : "OVER_QUERY_LIMIT"
}

請至https://console.developers.google.com/apis/credentials 建立一組API金鑰並啟用Google Maps Geocoding API,至少可以呼叫的額度會提升一些