2012年11月26日 星期一

Develop Google Maps API v3 Map Pages with ASP.NET AJAX


  • 前言:Google Maps API 提供服務以來,由於容易開發、API功能多更新快並且又穩定,只要不踩到一些地雷,就不會收錢,所以一直是地圖開發者喜歡用的工具API。這一篇是要介紹,從資料庫取得點位資料套疊到地圖上。我使用的工具是 ASP.Net 使用 Google Map API v3 ,並且用AJAX,是因為如果用標準的asp.net button,會submit/postback 回 server,如果使用者在submit之前就做了一些地圖操作(放大、移動..),就會因為submit to server 會回到初始畫面,這樣的操作模式是滿令人不悅的,所以才需要AJAX方式來處理使用者對地圖的操作。
  • 網頁功能:取得使用者目前座標,繪製地點與精確度的範圍。並根據使用者點選的圖層類型,帶入對應的點位。
  • 瀏覽器限制:因為有用到HTML5,IE8,9確定不行,用 Chrome Ok, IPhone Safari OK
  • 使用元件:
    • Google Map API V3 :必要
    • Visual Studio 2012:必要
    • JQuery mobile:非必要,放著只為了可以在行動裝置上看。
    • HTML5:必要,為了取得目前座標。所以..那個 IE
  • 網頁主要分成 3 區 
    • (1)地圖區:顯示地圖
    • (2) ASP.Net button 區:負責從資料庫取得資料,並呈現在地圖
    • (3) JS Button區:主要是 javascript button,是操作google map的功能,如"取得目前位置"、"清除地圖" 這類的功能

  • 以下列出比較重要的程式片段,完整的程式請到網頁最下方再去下載。
  • HTML Head區:需要注意的是 style 那一區,因為 Google Map DIV必須指定大小,所以指定在Head裡面。其他就是引用 jquery 與 google map api v3
<head>
    <title>Google Maps API v3 and ASP.Net AJAX</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1" /> 
    <style type="text/css">
      html { height: 100% }
      body { height: 100%; margin: 0px; padding: 0px }
      #map_canvas { height: 100% }
    </style>
 <link rel="stylesheet" href="/css/jquery.mobile-1.2.0.min.css" />
    <script src="/js/jquery-1.8.2.min.js"></script>
    <script src="/js/jquery.mobile-1.2.0.min.js"></script>
    <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>

  • 再下來就是控制google maps div 的區域,首先是global的變數
        //Global variables
        var map;            //地圖
        var marker;         //地圖上的點
        var infowindow;     //點上面跳出的視窗
        var overlays = [];  //目前地圖上所有套疊的圖層
        var mapcenter = new google.maps.LatLng(25.228664, 121.750202);  //預設的地圖中心

  • 地圖初始化的function,等下會在body onload呼叫。裡面的詳細API 請參考Google 文件。在初始化完成後,呼叫 取得目前座標的function: getLocation。
        function initialize() {
            var mapOptions = {
                zoom: 6,
                mapTypeId: google.maps.MapTypeId.ROADMAP,
                center: mapcenter,
                mapTypeControl: true,
                mapTypeControlOptions:
                {
                    style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
                    poistion: google.maps.ControlPosition.TOP_RIGHT,
                    mapTypeIds: [google.maps.MapTypeId.ROADMAP,
                    google.maps.MapTypeId.TERRAIN,
                    google.maps.MapTypeId.HYBRID,
                    google.maps.MapTypeId.SATELLITE]
                },
                navigationControl: true,
                navigationControlOptions:
                {
                    style: google.maps.NavigationControlStyle.ZOOM_PAN
                },
                scaleControl: true,
                disableDoubleClickZoom: false,
                streetViewControl: true,
                draggableCursor: 'move'
            };

            infowindow = null;
            infowindow = new google.maps.InfoWindow({
                content: "info window content"
            });

            map = new google.maps.Map(document.getElementById('map_canvas'),
            mapOptions);
            
            //取得使用者目前座標
            getLocation();
        }

  • 取得目前位置化的function:使用HTML5的方式取得座標(點)與精確度(圓圈),並繪製到地圖上。
        //
        //取得目前位置
        //
        function getLocation() {
            var x = document.getElementById("Message");
            if (navigator.geolocation) {
                navigator.geolocation.getCurrentPosition(showPosition);
                //如果要持續取得,用watchPosition
//navigator.geolocation.watchPosition(showPosition); } else { x.innerHTML = "Geolocation is not supported by this browser."; } } function showPosition(position) { var x = document.getElementById("Message"); //x.innerHTML = "Latitude: " + position.coords.latitude + " Longitude: " + position.coords.longitude + " Accuracy" + position.coords.accuracy; var newMapCenter = new google.maps.LatLng(position.coords.latitude, position.coords.longitude); map.setCenter(newMapCenter); //以marker方式出現 setMarker(newMapCenter, "My Current Location" + " Accuracy is :" + position.coords.accuracy); //以圓形出現 if (position.coords.accuracy != null) { DrawCircle(newMapCenter, position.coords.accuracy); if (position.coords.accuracy < 1000) { map.setZoom(14); } } else { DrawCircle(newMapCenter, 100); } }
  • 繪圖用的function,繪製圓形、繪製單一點座標與視窗(infowindow)訊息
        //繪製圓形
        function DrawCircle(center,rad) {
            var draw_circle;
            draw_circle = new google.maps.Circle({
                center: center,
                radius: rad,
                strokeColor: "#FF0000",
                strokeOpacity: 0.8,
                strokeWeight: 2,
                fillColor: "#FFFF00",
                fillOpacity: 0.35,
                map: map
            });
            overlays.push(draw_circle);
        }

        //繪製單一點座標
        function setMarker(singleCoord,infoWindowContent) {
            marker = new google.maps.Marker({
                map: map,
                draggable: true,
                animation: google.maps.Animation.DROP,
                position: singleCoord
            });

            google.maps.event.addListener(marker, 'click', function () {
                infowindow.setContent(infoWindowContent);
                infowindow.open(map, this);
            });

            //
            overlays.push(marker);
        }

        //產生彈跳效果
        function toggleBounce() {
            if (marker.getAnimation() != null) {
                marker.setAnimation(null);
            } else {
                marker.setAnimation(google.maps.Animation.BOUNCE);
            }
        }
  • 繪製多個點,提供給asp.net程式呼叫。Javascript Array的格式是['Mount Evans', 59.32522, 18.17002, 4, 'This is Mount Evans.'];,C#要繪製多點的時候,就是去準備出這個多筆陣列,再交給 setMarkers 去繪製。
        function setMarkers(markers) {
            for (var i = 0; i < markers.length; i++) {
                var sites = markers[i];
                var siteLatLng = new google.maps.LatLng(sites[1], sites[2]);
                var marker = new google.maps.Marker({
                    position: siteLatLng,
                    map: map,
                    title: sites[0],
                    zIndex: sites[3],
                    html: sites[4]
                });

                var contentString = "info window content";

                google.maps.event.addListener(marker, "click", function () {
                    infowindow.setContent(this.html);
                    infowindow.open(map, this);

                    if (marker.getAnimation() != null) {
                        marker.setAnimation(null);
                    } else {
                        marker.setAnimation(google.maps.Animation.BOUNCE);
                    }
                });

                //
                overlays.push(marker);
            }
        }
  • 清除全部繪製的圖層
        function clearMarkers() {
            while (overlays[0]) {
                overlays.pop().setMap(null);
            }
        }
  • 另外在.ASPX網頁設計上,只有ASPX的Controls(Button1~4,顯示A~D類的圖層資料),放在UpdatedPanel裡面。Google Map DIV 更要在Form 以外才能正常顯示。控制地圖的HTML Button(Button 6,7)則是單純的HTML button就可以
<div id="Message"></div>
    <div id="map_canvas" style="width:100%; height:80%"></div>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
                AJAX Button:<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="All" />
                <asp:Button ID="Button2" runat="server" OnClick="Button2_Click" Text="A" />
                <asp:Button ID="Button3" runat="server" OnClick="Button3_Click" Text="B" />
                <asp:Button ID="Button4" runat="server" OnClick="Button4_Click" Text="C" />
                <asp:Label ID="Label1" runat="server" Visible="False"></asp:Label>
                <br />
                <asp:Label ID="lbErr" runat="server" Visible="False"></asp:Label>
                <br />
            </ContentTemplate>
        </asp:UpdatePanel>
        JS Button:
        <input id="Button5" type="button" value="Find My Location" onclick="getLocation()"/><input id="Button6" type="button" value="Clear Markers" onclick="clearMarkers()" /></form>
  • C#程式端,以Button3為例,這個按鈕的目的是取得B類的座標並繪製於地圖上
    protected void Button3_Click(object sender, EventArgs e)
    {
        Label1.Text = System.DateTime.Now.ToLongTimeString();
        Label1.Visible = true;

        string strJS = "";
        ArrayList alMarkers = GetMarkers("B");

        strJS = @"var sites = [" + String.Join(",", alMarkers.ToArray()) + "];setMarkers(sites);";
        ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "alert", strJS, true);
    }
  • C#程式端,GetMarkers的目的是從資料庫取出對應的
protected ArrayList GetMarkers(string strType)
    {
        ArrayList alMarkers = new ArrayList();
        try
        {
            string conn = ConfigurationManager.ConnectionStrings["mydevConnectionString"].ConnectionString;
            using (SqlConnection connection = new SqlConnection(conn))
            {
                connection.Open();

                SqlCommand command = connection.CreateCommand();
                command.Connection = connection;

                if (strType == "ALL")
                {
                    command.CommandText = @"SELECT * FROM [MyDev].[dbo].[MyMap] with (nolock)";
                }
                else
                {
                    command.CommandText = @"SELECT * FROM [MyDev].[dbo].[MyMap] with (nolock) where Category=@Category";
                    command.Parameters.Add("@Category", SqlDbType.NVarChar, 20).Value = strType;
                }

                using (SqlDataReader dr = command.ExecuteReader())
                {
                    while (dr.Read())
                    {
                        alMarkers.Add("['" + dr["Project"].ToString() + "', " + dr["Latitude"].ToString() + "," + dr["Longtitude"].ToString() + ", 1, '" + dr["Info"].ToString() + "
" + dr["Addr"].ToString() + "']");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            lbErr.Text = ex.ToString();
            lbErr.Visible = true;
        }
        return alMarkers;
    }

  • Table的設計很簡單,主要就是 Latitude(緯度), Longtitude(經度),其他都是輔助描述的欄位
CREATE TABLE [dbo].[MyMap](
 [ID] [bigint] IDENTITY(1,1) NOT NULL,
 [Category] [nvarchar](20) NOT NULL,
 [Code] [nvarchar](20) NULL,
 [Info] [nvarchar](200) NULL,
 [Project] [nvarchar](50) NULL,
 [Addr] [nvarchar](100) NOT NULL,
 [Latitude] [float] NOT NULL,
 [Longtitude] [float] NOT NULL,
 [Coord] [geography] NULL
) ON [PRIMARY]


完成,結果如圖:
一進入網頁時,會直接定位目前座標,My Current Location 是我目前的座標,黃色是72公尺的精準度,其他的marker是點了B,帶入B類的座標,顯示在地圖上。

歡迎討論!

程式下載:
https://dl.dropbox.com/u/3330791/Sharecode/GoogleMapV3_ASPNET.rar

reference:
https://developers.google.com/maps/documentation/javascript/reference

3 則留言:

  1. 哈囉你好,我目前的問題卡在我點了button後地圖會重新初始化,請問這要怎麼解決呢?剛剛接觸到asp.net所以不是很熟悉,ajax完全一竅不通,現在把資料庫的座標用json動態撈點和畫線,卻發現一直會初始化,有比較簡單的方式可以解決嗎?謝謝。

    回覆刪除
  2. hello 點了Button就會reset 是因為postback到server,所以一直重新初始化.Ajax就是為了解決這個狀況。
    如果對AJAX不熟悉,你可以改用全Javascript的方式,Button 改用 HTML Button(不submit的)而不是 .Net Button,這樣就不會postback。而HTML Button裡面就可以取JSON 資料,然後對 GMap 操作畫圖了。
    如果對AJAX有點認識了,可以直接下載程式去看,了解運作情形,應該就可以解決你的問題了!

    回覆刪除
  3. 不好意思, 你提供下載的檔案,好像不是這個專案的. 好像是網頁縮圖的.可以麻煩你分享嗎?謝謝

    回覆刪除