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

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

延續上一篇文章:[ASP.net WebForm/Google] 在Google Map上放置多個標記地點(Marker)/API 3版

這次要想辦法把Marker更改icon

(順道一提,我一直以為使用Google Map API的話,網站一定要對外開放,目前發現只要本機電腦網路有通

有引用<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script> 就可以玩了)

另外,為了配合專案需求,程式碼有稍做重構且追加DB資料


Create database GoogleMapDB
Go
Use GoogleMapDB
Go
/*在C#的緯度經度型別為double,在SQL Server的型別為float
  參考:http://stackoverflow.com/questions/1440620/which-sql-server-data-type-best-represents-a-double-in-c
*/
Create table tb_company
(
 id int identity primary key,/*主鍵*/
 zip_no int,/*郵遞區號*/
 company_title nvarchar(50),/*公司名稱*/
 [address] nvarchar(500),   /*公司地址*/
 lat float default(0) ,     /*公司所在緯度*/
 lng float default(0) ,     /*公司所在經度*/
 company_desc nvarchar(50),/*公司簡介*/
 iconName varchar(50) /*標記點的icon檔名*/
)
Go
Insert into tb_company (zip_no,company_title,[address],company_desc,iconName) values
(100,'精誠資訊','台北市中正區羅斯福路2段100號','在恆逸資訊上過課的日子受您照顧了<(_ _)>','flower.jpg'),
(100,'台灣師範大學','台北市和平東路一段162號','在捷運古亭站附近的大學','bar.jpg'),
(100,'捷運古亭站','台北市中正區羅斯福路2段164-1號','南京松山線啥時會開?','airplane.jpg')

 
 

image

新增一個DBUtil.cs類別,做為資料存取層


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Configuration;
using System.Data;
using System.Data.SqlClient;

/// <summary>
/// DBUtil 的摘要描述
/// </summary>
public class DBUtil
{

    //連線字串
    string connStr = WebConfigurationManager.ConnectionStrings["connStr"].ConnectionString;

    /// <summary>
    /// 傳入SQL語句,回傳DataTable物件
    /// </summary>
    /// <param name="sql"></param>
    /// <returns></returns>
    public DataTable queryDataTable(string sql)
    {
       
        DataSet ds = new DataSet();
        using (SqlConnection conn=new SqlConnection(this.connStr))
        {
            SqlDataAdapter da = new SqlDataAdapter(sql,conn);
            da.Fill(ds);
        }
        return ds.Tables.Count > 0 ? ds.Tables[0] : new DataTable();
    }


    
}

 

 

用來輸出JSON字串的getSpot.ashx


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

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 getSpot : IHttpHandler {

    int zip_no = 100;//中正區的郵遞區號
    DBUtil db = new DBUtil();
    
    public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "text/plain";
        if (!string.IsNullOrEmpty(context.Request["zip_no"]))
        {
            int.TryParse(context.Request["zip_no"], out this.zip_no);//防SQL Injection,轉型別失敗就用預設值
        }

         
        //取得DataTable原始資料
        DataTable dt = db.queryDataTable(@"SELECT zip_no,company_title,address,company_desc,iconName,lat,lng
                                            from tb_company
                                            Where zip_no="+this.zip_no+@"
                                            Order by ID ASC");
        //因為本範例的資料都沒有緯度和經度,所以把原始資料DataTable傳入取得一個新的DataTable(有緯度、經度的)
        DataTable dt_new  = this.fillLatLng(dt);
        //利用Json.NET將DataTable轉成JSON字串,請參考另一篇文章:http://www.dotblogs.com.tw/shadow/archive/2011/11/30/60083.aspx
        string str_json = JsonConvert.SerializeObject(dt_new, Formatting.Indented);
        context.Response.Write(str_json);
    }
 
    /// <summary>
    /// 回傳有緯度和經度的DataTable
    /// </summary>
    /// <returns></returns>
     private DataTable fillLatLng(DataTable dt)
    {

        DataTable dt_new = dt.Copy();
        for (int i=0;i<dt.Rows.Count;i++)//走訪原始資料
        {
          string json_address=  this.convertAddressToJSONString(dt.Rows[i]["address"].ToString());
          //取得緯度和經度
          double[] latLng = this.getLatLng(json_address);
          dt_new.Rows[i]["lat"] = latLng[0];
          dt_new.Rows[i]["lng"] = latLng[1];
            
        }


        return dt_new;
    }


    /// <summary>
    /// 傳入JSON字串,取得經緯度
    /// </summary>
    /// <param name="json"></param>
    /// <returns></returns>
     private double[] getLatLng(string json)
     {
         JObject jo = JsonConvert.DeserializeObject<JObject>(json);

         //從results開始往下找
         JArray ja = (JArray)jo.Property("results").Value;
         jo = ja.Value<JObject>(0);//JArray第0筆
         //得到location的JObject
         jo = (JObject)((JObject)jo.Property("geometry").Value).Property("location").Value;
         
         //緯度
         double lat = Convert.ToDouble(((JValue)jo.Property("lat").Value).Value);
         //經度
         double lng = Convert.ToDouble(((JValue)jo.Property("lng").Value).Value);
         
         double[] latLng= {lat,lng};

         return latLng;
     
     } 
      
    /// <summary>
     /// 把地址轉成JSON格式,這樣資訊裡才有經緯度
     /// 因為使用到地理編碼技術,請注意使用限制:http://code.google.com/intl/zh-TW/apis/maps/documentation/geocoding/#Limits
    /// </summary>
    /// <param name="address"></param>
    /// <returns></returns>
     private string convertAddressToJSONString(string address)
     {
         //var url = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=" + HttpContext.Current.Server.UrlEncode(address);
         //2012.4.12 以上是舊寫法,新寫法請用以下
        var url =    "http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=" + HttpContext.Current.Server.UrlEncode(address);


         string result = String.Empty;
         HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
         using (var response = request.GetResponse())
         using (StreamReader sr = new StreamReader(response.GetResponseStream()))
         {

             result = sr.ReadToEnd();
         }
         return result;

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

}

執行結果:

image

Default.aspx的jQuery Ajax和Google Map API寫法都稍做修改,容易閱讀


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

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>
    <script type="text/javascript" src="Scripts/jquery-1.4.1.min.js"></script>
    <!--要玩Google Map 就一定要引用此js-->
    <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
    <script type="text/javascript">
        $(document).ready(init);

        //網頁上所有的DOM都載入後
        function init() {
            <%--中正區郵遞區號,這裡可以抓Code-Behind的 protected string zip_no;來替代--%> 
            var zip_no = '100';
            
            //取得標記點資料
            getMapData(zip_no);
        }
        
        //取得標記點資料
        function getMapData(zip_no) {
            $.ajax(
            {
                url: 'getSpot.ashx',
                type: 'post',
                async: false,
                data: { zip_no: zip_no },
                dataType: 'json',
                success: addMarker,//在某一區加入多個標記點
                error:function(e){
                      alert("Google地圖發生錯誤,無法載入");
                }
            });//End jQuery Ajax
        } //End function getMapData(zip_no)

       

        var map;
        //在某一區加入多個標記點
        function addMarker(str_json)
        {
           //是否為第一次執行迴圈
           var first = true;    
            for (var index in str_json) 
            {

            //建立緯經度座標物件
            var latlng = new google.maps.LatLng(str_json[index].lat, str_json[index].lng);


                if (first == true) 
                {//第一次執行迴圈
                    /*以哪個緯經度中心來產生地圖*/
                    var myOptions = {
                        zoom: 14,
                        center: latlng,
                        mapTypeId: google.maps.MapTypeId.ROADMAP
                    };
                    /*產生地圖*/
                    map = new google.maps.Map($("div#div_showMap")[0], myOptions);

                    first = false;
                } //End if (first == true) 

                //加入一個Marker到map中
                var marker = new google.maps.Marker({
                    position: latlng,
                    map: map,
                    title: str_json[index].company_title
                });
            } //End for (var index in str_json) 
        }//End function addMarker()
    </script>
</head>
<body>
    <form id="form1" runat="server">

    <!--顯示Google Map的區塊-->
    <div id="div_showMap" style="width:672px;height:473px;">
    </div>
    </form>
</body>
</html>

Default.aspx.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

/*Code-Behind沒什麼要做的*/
public partial class _Default : System.Web.UI.Page
{
    protected string zip_no = "100";

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)//Get Method要做的事
        {
            
        }

    }

}

 

 

執行Default.aspx結果:

image

環境都準備好了,開始動工

image

更改icon圖示很簡單只要加上以下粗體字就行了


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

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>
    <script type="text/javascript" src="Scripts/jquery-1.4.1.min.js"></script>
    <!--要玩Google Map 就一定要引用此js-->
    <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
    <script type="text/javascript">
        $(document).ready(init);

        //網頁上所有的DOM都載入後
        function init() {
            <%--中正區郵遞區號,這裡可以抓Code-Behind的 protected string zip_no;來替代--%> 
            var zip_no = '100';
            
            //取得標記點資料
            getMapData(zip_no);
        }
        
        //取得標記點資料
        function getMapData(zip_no) {
            $.ajax(
            {
                url: 'getSpot.ashx',
                type: 'post',
                async: false,
                data: { zip_no: zip_no },
                dataType: 'json',
                success: addMarker,//在某一區加入多個標記點
                error:function(e){
                      alert("Google地圖發生錯誤,無法載入");
                }
            });//End jQuery Ajax
        } //End function getMapData(zip_no)

       

        var map;
        //在某一區加入多個標記點
        function addMarker(str_json)
        {
           //是否為第一次執行迴圈
           var first = true;    
            for (var index in str_json) 
            {

            //建立緯經度座標物件
            var latlng = new google.maps.LatLng(str_json[index].lat, str_json[index].lng);


                if (first == true) 
                {//第一次執行迴圈
                    /*以哪個緯經度中心來產生地圖*/
                    var myOptions = {
                        zoom: 14,
                        center: latlng,
                        mapTypeId: google.maps.MapTypeId.ROADMAP
                    };
                    /*產生地圖*/
                    map = new google.maps.Map($("div#div_showMap")[0], myOptions);

                    first = false;
                } //End if (first == true) 

                //加入一個Marker到map中
                  var imageUrl = "";//空字串就會使用預設圖示
                if(str_json[index].iconName!="")//有值
                {
                //可以是相對路徑也可以是http://開頭的絕對路徑,路徑錯誤的話圖示不會出來
                    imageUrl = "images/" + str_json[index].iconName;
                }
                
                var marker = new google.maps.Marker({
                    position: latlng,
                    map: map,
                    title: str_json[index].company_title,
                    icon:imageUrl
                });
            } //End for (var index in str_json) 
        }//End function addMarker()
    </script>
</head>
<body>
    <form id="form1" runat="server">

    <!--顯示Google Map的區塊-->
    <div id="div_showMap" style="width:672px;height:473px;">
    </div>
    </form>
</body>
</html>

 

 

執行結果:

image

接著針對點擊Click每個標記點,就要出現對話框

image

這邊如果真的照Google API 範例寫


  var map;
        //在某一區加入多個標記點
        function addMarker(str_json)
        {
           //是否為第一次執行迴圈
           var first = true;    



             
           
            for (var index in str_json) 
            {

            //建立緯經度座標物件
            var latlng = new google.maps.LatLng(str_json[index].lat, str_json[index].lng);


                if (first == true) 
                {//第一次執行迴圈
                    /*以哪個緯經度中心來產生地圖*/
                    var myOptions = {
                        zoom: 14,
                        center: latlng,
                        mapTypeId: google.maps.MapTypeId.ROADMAP
                    };
                    /*產生地圖*/
                    map = new google.maps.Map($("div#div_showMap")[0], myOptions);

                    first = false;
                } //End if (first == true) 

                //加入一個Marker到map中
                var imageUrl = "";//空字串就會使用預設圖示
                if(str_json[index].iconName!="")//有值
                {
                //可以是相對路徑也可以是http://開頭的絕對路徑,路徑錯誤的話圖示不會出來
                    imageUrl = "images/" + str_json[index].iconName;
                }
                
                var marker = new google.maps.Marker({
                    position: latlng,
                    map: map,
                    title: str_json[index].company_title,
                    icon:imageUrl
                });
                
               
                /*資訊視窗*/
               var infowindow = new google.maps.InfoWindow({content:str_json[index].company_desc}); 
                
              
                google.maps.event.addListener(marker, 'click', function() { 
                  infowindow.open(map,marker); });
            } //End for (var index in str_json) 
        }//End function addMarker()

 

 

 

會發現不論點擊哪個標記點,對話框(資訊視窗)都在同一個位置,且內文都一樣

image

所以參考此篇做Bug修正:Google Maps API v3 – Multiple Markers, Multiple Infowindows

完整的Default.aspx


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

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>
    <script type="text/javascript" src="Scripts/jquery-1.4.1.min.js"></script>
    <!--要玩Google Map 就一定要引用此js-->
    <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
    <script type="text/javascript">
        $(document).ready(init);

        //網頁上所有的DOM都載入後
        function init() {
            <%--中正區郵遞區號,這裡可以抓Code-Behind的 protected string zip_no;來替代--%> 
            var zip_no = '100';
            
            //取得標記點資料
            getMapData(zip_no);
        }
        
        //取得標記點資料
        function getMapData(zip_no) {
            $.ajax(
            {
                url: 'getSpot.ashx',
                type: 'post',
                async: false,
                data: { zip_no: zip_no },
                dataType: 'json',
                success: addMarker,//在某一區加入多個標記點
                error:function(e){
                      alert("Google地圖發生錯誤,無法載入");
                }
            });//End jQuery Ajax
        } //End function getMapData(zip_no)

       

        var map;
        //在某一區加入多個標記點
        function addMarker(str_json)
        {
           //是否為第一次執行迴圈
           var first = true;    
             /*資訊視窗放在迴圈外,content由標記點的html決定*/
             var infowindow = new google.maps.InfoWindow(); 
            for (var index in str_json) 
            {

            //建立緯經度座標物件
            var latlng = new google.maps.LatLng(str_json[index].lat, str_json[index].lng);


                if (first == true) 
                {//第一次執行迴圈
                    /*以哪個緯經度中心來產生地圖*/
                    var myOptions = {
                        zoom: 14,
                        center: latlng,
                        mapTypeId: google.maps.MapTypeId.ROADMAP
                    };
                    /*產生地圖*/
                    map = new google.maps.Map($("div#div_showMap")[0], myOptions);

                    first = false;
                } //End if (first == true) 

                //加入一個Marker到map中
                var imageUrl = "";//空字串就會使用預設圖示
                if(str_json[index].iconName!="")//有值
                {
                //可以是相對路徑也可以是http://開頭的絕對路徑,路徑錯誤的話圖示不會出來
                    imageUrl = "images/" + str_json[index].iconName;
                }
                
                var marker = new google.maps.Marker({
                    position: latlng,
                    map: map,
                    title: str_json[index].company_title,
                    icon:imageUrl,
                    html: str_json[index].company_desc 
                });

                 //為每個標記點註冊click事件
               google.maps.event.addListener(marker, 'click', function() { 
                 
                  /*this就是指marker*/
                  infowindow.setContent(this.html);
                  infowindow.open(map,this);
                  
                 
                   });//End click function
            } //End for (var index in str_json) 
        }//End function addMarker()


        

    </script>
</head>
<body>
    <form id="form1" runat="server">

    <!--顯示Google Map的區塊-->
    <div id="div_showMap" style="width:672px;height:473px;">
    </div>
    </form>
</body>
</html>

 

執行結果:

image

image

image

 

 

完整研究包

 

2011.12.02 追記,標記點一次最多似乎只能標10個?2013.02.01 追記[ASP.net WebForm/Google] 解決Google API地理編碼技術地址轉JSON要求上限10筆問題

另外Marker是可以自訂屬性的,例如以下粗體字


              //加入一個Marker到map中
                var marker = new google.maps.Marker({
                    position: latlng,
                    map: map,
                    title: str_json[index].通訊地址,
                    html: str_json[index].稱謂,
                    LABOR_ID:str_json[index].LABOR_ID
                });

                 //為每個標記點註冊click事件
               google.maps.event.addListener(marker, 'click', function() { 
                 
                  /*this就是指marker,當click標記點時,超連結導向其他頁*/
                  window.location.href = "persondetail.html?LABOR_ID="+this.LABOR_ID;
                  
                 
                   });//End click function

 

 

 

2011.12.03 追記一個自己寫的類別,專門處理Google Map API業務


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net;
using System.IO;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Data;

/// <summary>
/// Create by Shadow at 2011.12.03
/// 參考文章:http://www.dotblogs.com.tw/shadow/archive/2011/02/18/21442.aspx
/// http://www.dotblogs.com.tw/shadow/archive/2011/12/02/60381.aspx
/// http://www.dotblogs.com.tw/shadow/archive/2011/12/02/60479.aspx
/// </summary>
public class GoogleMapUtility
{

    /// <summary>
    /// 回傳有緯度和經度的DataTable
    /// </summary>
    /// <returns></returns>
    public static DataTable fillLatLng(DataTable dt, string addressColumnName)
    {
        /*先把原始結構和資料Copy到一份新的DataTable*/
        DataTable dt_new = dt.Copy();
        dt_new.Columns.Add("lat", typeof(double));
        dt_new.Columns.Add("lng", typeof(double));

        for (int i = 0; i < dt.Rows.Count; i++)//走訪原始資料
        {
            string json_address = GoogleMapUtility.convertAddressToJSONString(dt.Rows[i][addressColumnName].ToString());
            //取得緯度和經度
            double[] latLng = GoogleMapUtility.getLatLng(json_address);
            dt_new.Rows[i]["lat"] = latLng[0];
            dt_new.Rows[i]["lng"] = latLng[1];

        }


        return dt_new;
    }


    /// <summary>
    /// 傳入Geocoding API產生的地址JSON字串,取得經緯度
    /// </summary>
    /// <param name="json"></param>
    /// <returns></returns>
    public static double[] getLatLng(string json)
    {
        JObject jo = JsonConvert.DeserializeObject<JObject>(json);

        //從results開始往下找
        JArray ja = (JArray)jo.Property("results").Value;
        jo = ja.Value<JObject>(0);//JArray第0筆
        //得到location的JObject
        jo = (JObject)((JObject)jo.Property("geometry").Value).Property("location").Value;

        //緯度
        double lat = Convert.ToDouble(((JValue)jo.Property("lat").Value).Value);
        //經度
        double lng = Convert.ToDouble(((JValue)jo.Property("lng").Value).Value);

        double[] latLng = { lat, lng };

        return latLng;

    }

    /// <summary>
    /// 把地址轉成JSON格式,這樣資訊裡才有緯經度
    /// 因為使用到Geocoding API地理編碼技術,請注意使用限制:http://code.google.com/intl/zh-TW/apis/maps/documentation/geocoding/#Limits
    /// </summary>
    /// <param name="address"></param>
    /// <returns></returns>
    public static string convertAddressToJSONString(string address)
    {
        //var url = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=" + HttpContext.Current.Server.UrlEncode(address);
       //2012.4.12 以上是舊寫法,新寫法請用以下
        var url =   "http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=" + HttpContext.Current.Server.UrlEncode(address);
        string result = String.Empty;
        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
        using (var response = request.GetResponse())
        using (StreamReader sr = new StreamReader(response.GetResponseStream()))
        {

            result = sr.ReadToEnd();
        }
        return result;

    }
}

使用方法(以.ashx為例)


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

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;
//如何於泛型處理常式 .ashx 中存取工作階段變數(Session Variable)   
using System.Web.SessionState;
//http://www.limingchstudio.com/2009/05/ashx-session-variable.html  
public class GetPeopleMapData : IHttpHandler, IRequiresSessionState 
{

     
    
    public void ProcessRequest (HttpContext context) {
        
        

        context.Response.ContentType = "text/plain";
        //從DB取得DataTable原始資料
        DataTable dt = new DataTable();
        //假設因為資料都沒有緯度和經度,所以把原始資料DataTable傳入此方法再取得一個新的DataTable(有緯度、經度的)
        DataTable dt_new  = GoogleMapUtility.fillLatLng(dt,"address");//字串address為資料表的地址欄位名稱
        //利用Json.NET將DataTable轉成JSON字串,請參考另一篇文章:http://www.dotblogs.com.tw/shadow/archive/2011/11/30/60083.aspx
        string str_json = JsonConvert.SerializeObject(dt_new, Formatting.Indented);
        context.Response.Write(str_json);//輸出JSON字串
    }
 
    
    public bool IsReusable {
        get {
            return false;
        }
    }

}

 

2012.2.3 追記 衍伸閱讀文章:

 

2012.4.12 要注意預設標記點是張img圖片

今天發現ART人員定義了一個CSS樣式

#div_showMap img{padding:6.5em 7.2em;}

在IE8上瀏覽的話,標記點會消失

然後再補一個Google地理編碼專用,用來檢查回傳Result結果狀態是否為OK字串


/// <summary>
    /// 傳入json字串,判斷回傳結果Result是否為OK
    /// </summary>
    /// <param name="json"></param>
    /// <returns></returns>
    private static bool isResultOk(string json)
    {
        JObject jo = JsonConvert.DeserializeObject<JObject>(json);

          
        return jo["status"].ToString().ToUpper() == "OK";
    }