[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筆的話
程式會出錯
因為之前範例都提搭配Google API地理編碼技術(地址轉JSON)來標記Marker
Google API地理編碼技術一次要求太頻繁的話,API會發生OVER_QUERY_LIMIT錯誤
趁著今天有空,想了一個使用者體驗較好的解決方式,每使用一次地理編碼技術就延遲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,至少可以呼叫的額度會提升一些