ทำอย่างไรให้สามารถกำหนดจุดพิกัดบนแผนที่ Google map แบบจุดเดียวและหลายจุดจากฐานข้อมูลได้ด้วย ASP.NET C# (ภาคต่อ)

             จากบทความที่แล้ว ผู้เขียนได้เขียนไว้เกี่ยวกับเรื่องวิธีการกำหนดจุดพิกัดบนแผนที่กันไปบ้างแล้ว ในหัวข้อ “ทำอย่างไรให้สามารถกำหนดจุดพิกัดบนแผนที่ Google map แบบจุดเดียวและหลายจุดจากฐานข้อมูลได้ด้วย ASP.NET C#” สำหรับในบทความนี้ผู้เขียนจึงขอพูดถึงในส่วนของการดึงค่าละติจูด ลองจิจูดของสถานที่ ซึ่งนับว่าเป็นส่วนประกอบสำคัญในการแสดงผลพิกัดบนแผนที่ ซึ่งเดิมทีแล้วนั้น ผู้ใช้อาจต้องค้นหาข้อมูลพิกัดดังกล่าวจาก Google map เองและนำพิกัดดังกล่าวมากรอกลงฐานข้อมูลหรือมาระบุเพื่อการแสดงพิกัดนั้นๆในการเขียนโปรแกรม คงเป็นการดี หากการแสดงผลพิกัดจากฐานข้อมูลนั้น จะมีตัวช่วยอำนวยความสะดวกให้กับผู้ใช้ในการดึงค่าละติจูด และลองจิจูดโดยการกรอกข้อมูลชื่อสถานที่ลงไปเพื่อใช้ในการค้นหา ซึ่งน่าจะเป็นประโยชน์และทำให้ผู้ใช้สามารถใช้งานได้ง่ายขึ้นในการนำพิกัดเหล่านั้นไประบุบนแผนที่นั่นเอง

             โดยการดึงค่าพิกัดละติจูด-ลองจิจูดของสถานที่ สามารถทำได้หลายวิธี ขึ้นอยู่กับการนำไปประยุกต์ใช้ ซึ่งในบทความนี้ผู้เขียนขอแนะนำ 2 วิธี ดังนี้

  • การเรียกใช้เซอร์วิสของ Google Geocoding API  โดยการส่งพารามิเตอร์เป็นที่อยู่ของสถานที่ดังกล่าว

ฝั่ง C#

    private void getLatAndLong()
    {
        try
        {
         ////เป็นการกำหนด url ที่จะใช้ในการเรียกเซอร์วิสของ Google Geocoding API 
โดยมีการส่งค่าพารามิเตอร์เป็นข้อมูลที่อยู่
            string url = "http://maps.google.com/maps/api/geocode/xml?address=" + txtLocation.Text + "&sensor=false";
            WebRequest request = WebRequest.Create(url);
            using (WebResponse response = (HttpWebResponse)request.GetResponse())
            {
        ////ผลลัพธ์จะอยู่ในรูปแบบของ XML หรือ JSON และจะถูกอ่านให้อยู่ในรูปแบบ Dataset 
โดยใช้ StreamReader
                 using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                {
                    DataSet dsResult = new DataSet();
                    dsResult.ReadXml(reader);
       ////จัดทำโครงสร้างตาราง(datatable) ที่จะใช้ในการแสดงผลใน Gridview
                    DataTable dtCoordinates = new DataTable();
                    dtCoordinates.Columns.AddRange(new DataColumn[4] { new DataColumn("Id", typeof(int)),
                        new DataColumn("Address", typeof(string)),
                        new DataColumn("Latitude",typeof(string)),
                        new DataColumn("Longitude",typeof(string)) });

       ////ดึงค่าผลลัพธ์จากตารางต่างๆ เพื่อนำค่าที่จำเป็นมาแสดงผลตามต้องการ
                    foreach (DataRow row in dsResult.Tables["result"].Rows)
                    {
                        string geometry_id = dsResult.Tables["geometry"].Select("result_id = " + row["result_id"].ToString())[0]["geometry_id"].ToString();
                        DataRow location = dsResult.Tables["location"].Select("geometry_id = " + geometry_id)[0];
                         dtCoordinates.Rows.Add(row["result_id"],  row["formatted_address"], location["lat"], location["lng"]);
                    }
       ////แสดงผลข้อมูลของค่าที่ดึงมาได้ใน Gridview (ถ้ามี)
                    if (dtCoordinates.Rows.Count > 0)
                    {
                        gvLatLong.DataSource = dtCoordinates;
                        gvLatLong.DataBind();
                    }
                    else
                    {
                        gvLatLong.DataSource = null;
                        gvLatLong.DataBind();
                        ScriptManager.RegisterStartupScript(Page, Page.GetType(), "alert", "alert('Can not find Latitude and Longitude!'); ", true);

                    }
                }
            }
        }
        catch (Exception ex) {
            ScriptManager.RegisterStartupScript(Page, Page.GetType(), "alert", "alert('Can not find Latitude and Longitude!'); ", true);
            gvLatLong.DataSource = null;
            gvLatLong.DataBind();
        }
    
    }
 protected void btnSearch_Click(object sender, EventArgs e)
 {
    getLatAndLong();
 }

             จากโค้ดข้างต้น เป็นการค้นหาละติจูด-ลองจิจูดจากที่อยู่ของสถานที่นั้นๆ โดยใช้ Google Maps Geocoding API ซึ่งเซอร์วิสของ Google Geocoding API มีการส่งค่าโดยใช้ WebRequest โดยจะรับค่าที่อยู่ของสถานที่เป็นพารามิเตอร์และส่งกลับเป็นพิกัดและข้อมูลอื่นๆมาในรูปแบบของ XML หรือ JSON นั่นเอง และค่าที่ส่งกลับมาในรูปแบบ XML นั้นจะถูกอ่านให้อยู่ในรูปแบบ Dataset โดยใช้ StreamReader ซึ่งค่าที่ส่งกลับมานั้นจะมีด้วยกันหลายตาราง และมีการดึงค่าข้อมูลที่เกี่ยวข้องที่จะนำมาใช้เพิ่มลงในตารางที่ถูกสร้างขึ้นใหม่ และนำไปแสดงในกริดวิวนั่นเอง

ฝั่ง .aspx (Client side)

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
 <title>GetLatitude-Longitude</title>
<!-- การอ้างอิงเพื่อให้สามารถเรียกใช้งาน Autocomplete ได้-->
 <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places"></script>
</head>
<body>
 <form id="form1" runat="server">
 <div>
 <asp:TextBox ID="txtLocation" runat="server" Width="450px"></asp:TextBox><asp:Button ID="btnSearch" runat="server" Text="Search" OnClick="btnSearch_Click" />
 </div>
 <br />
 <div>
 <asp:GridView ID="gvLatLong" runat="server" CellPadding="4" ForeColor="#333333" GridLines="None">
 <AlternatingRowStyle BackColor="White" />
 <EditRowStyle BackColor="#2461BF" />
 <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
 <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
 <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
 <RowStyle BackColor="#EFF3FB" />
 <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
 <SortedAscendingCellStyle BackColor="#F5F7FB" />
 <SortedAscendingHeaderStyle BackColor="#6D95E1" />
 <SortedDescendingCellStyle BackColor="#E9EBEF" />
 <SortedDescendingHeaderStyle BackColor="#4870BE" />
 </asp:GridView>
 </div>
 </form>
 <script type="text/javascript">
 google.maps.event.addDomListener(window, 'load', function () {
////การกำหนดคอนโทรล textbox ที่จะเพิ่มความสามารถ place auto-complete 
เพื่อให้สามารถค้นหาสถานที่ตั้งได้ง่ายขึ้นเมื่อผู้ใช้พิมพ์ข้อมูลชื่อสถานที่ในกล่อง textbox
 var txtplaces = document.getElementById('<%= txtLocation.ClientID %>');
 var places = new google.maps.places.Autocomplete(document.getElementById('<%= txtLocation.ClientID %>'));
 });
 </script>
</body>
</html>

             จากโค้ดข้างต้น นอกจากจะเป็นการเรียกใช้เซอร์วิสโดยการคลิกปุ่ม “btnSearch” และรับค่าข้อมูลที่อยู่จากกล่อง textbox แล้ว ยังมีการเพิ่มส่วนของการเพิ่มความสามารถในการค้นหาข้อมูล “ที่อยู่” โดยการกรอกข้อมูล “ชื่อสถานที่” โดยใช้ feature ที่มีของ Google Places API (หรืออาจเรียกว่า place Autocomplete ) เนื่องจากหากมีการเรียกใช้เซอร์วิสดังกล่าวโดยตรง ผู้ใช้จำเป็นที่จะต้องกรอกข้อมูล “ที่อยู่” เพื่อเป็นพารามิเตอร์ให้กับเซอร์วิสที่เรียกใช้ให้ส่งค่าผลลัพธ์กลับมา ซึ่งความน่าจะเป็นที่ผู้ใช้จะสามารถทราบข้อมูลชื่อสถานที่นั้นย่อมเป็นไปได้มากกว่าการทราบข้อมูลที่อยู่ของสถานที่นั้นๆ ผู้เขียนจึงนำมาประยุกต์ใช้งานเข้าด้วยกันกับการเรียกเซอร์วิสที่กล่าวไว้แล้วก่อนหน้านั่นเอง โดยจะแนะนำวิธีการใช้งานเพิ่มเติมในหัวข้อต่อจากนี้

 เพิ่มเติม :
  Place Autocomplete: เป็นfeature ที่มีของ Google Places API เปรียบเสมือนตัวช่วยคำค้นเมื่อมีการป้อนข้อมูลชื่อสถานที่ลงไป โดย feature ดังกล่าวจะแสดงข้อมูลสถานที่ที่ใกล้เคียงกับคำค้นขึ้นมาให้กับผู้ใช้ได้เลือกนั่นเอง

โดยมีวิธีการเรียกใช้ place autocomplete  ดังนี้
1. อ้างอิงการเพื่อเรียกใช้งาน Google Places API

<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places"></script>

2. กำหนดคอนโทรล textbox ที่ต้องการเพิ่มความสามารถ place autocomplete

 var input = document.getElementById('<%= txtSearch.ClientID %>');
 var div = document.getElementById('result');
 var autocomplete = new google.maps.places.Autocomplete(input);

ตัวอย่างหน้าจอที่ได้เมื่อมีการพิมพ์คำค้น

place_service1

ผลลัพธ์

place_service2

เพิ่มเติม : Namespace ที่ต้องอ้างอิงเพิ่มเติมในการใช้งานโค้ดที่กล่าวไว้ข้างต้น มีดังนี้
            –  System.IO
            –  System.Net
            –  System.Data
            –  System.Text
  • แบบใช้ place Autocomplete ซึ่งเป็น feature ของ Google Places API ที่จะช่วยในการค้นหาที่อยู่จากชื่อสถานที่ได้และประยุกต์เพิ่มเติมเพื่อดึงค่ามาแสดงเมื่อมีการเลือกรายการสถานที่นั้นๆ
<!DOCTYPE html>
<html>
 <head>
<!-- การอ้างอิงเพื่อให้สามารถเรียกใช้งาน Autocomplete ได้-->
 <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places"></script>
 <title>Place Autocomplete</title>
 <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
 <meta charset="utf-8">
 </head>
 <body>
 <form runat="server">
 <asp:TextBox ID="txtSearch" runat="server" placeholder="Enter a location" Width="350px"></asp:TextBox> 
 <div id="result"></div>
 
 <script>
 function initialize() {
////การกำหนดคอนโทรล textbox ที่จะเพิ่มความสามารถ place auto-complete 
เพื่อให้สามารถค้นหาสถานที่ตั้งได้ง่ายขึ้นเมื่อผู้ใช้พิมพ์ข้อมูลชื่อสถานที่ในกล่อง textbox
 var input = document.getElementById('<%= txtSearch.ClientID %>');
 var div = document.getElementById('result');
 var autocomplete = new google.maps.places.Autocomplete(input);
 google.maps.event.addListener(autocomplete, 'place_changed', function () {

////ดึงค่าที่ได้เมื่อมีการเลือกรายการจากสถานที่ที่อยู่ที่ขึ้นมา และนำไปใช้ในการแสดงผล
 var place = autocomplete.getPlace();
 if (!place.geometry) {
 return;
 }
 else {
////นำค่าที่ดึงได้มาแสดงผลในพื้นที่ที่ต้องการ (result)
 div.innerHTML = '<br><div><strong>' + place.name + '</strong><br>' +
 '<strong>Address :</strong> ' + place.formatted_address + '<br><strong>Latitude :</strong> ' + place.geometry.location.lat() + ' <strong>Longitude:</strong>' + place.geometry.location.lng() + '</div>';
 }
 });
 }
 ////สั่งรันฟังก์ชั่น initialize() ตอนมีการโหลดหน้าจอ
 google.maps.event.addDomListener(window, 'load', initialize); 
 </script>
 </form>
 </body>
</html>

             จากโค้ดข้างต้น เป็นการดึงค่าละติจูดและลองจิจูดโดยใช้ feature ที่มีของ Google Places API (place Autocomplete) เพื่ออำนวยความสะดวกให้กับผู้ใช้ในการพิมพ์ชื่อสถานที่ลงใน textbox โดย feature นี้จะมีความสามารถในการดึงข้อมูลสถานที่และชื่อที่ใกล้เคียงขึ้นมาแสดงให้ผู้ใช้เลือก(ตามที่ได้กล่าวมาแล้วข้างต้น) และได้มีการสร้าง event เพิ่มเติมเมื่อผู้ใช้เลือกรายการใดขึ้นมา ก็จะมีการเรียกใช้เมธอด getPlace() เพื่อดึงค่าข้อมูลต่างๆมาใช้งานต่อไปรวมถึงค่าละติจูดและลองจิจูดที่เราต้องการใช้ในการกำหนดพิกัดด้วย

ตัวอย่างหน้าจอการค้นหาข้อมูลที่อยู่ เมื่อมีการพิมพ์คำค้น

place_auto1

ผลลัพธ์ที่ได้จากการค้นหา

place_auto2

             วิธีการที่ได้กล่าวไว้ในบทความนี้เป็นเพียงแนวทางให้กับผู้พัฒนาสามารถนำไปประยุกต์ใช้ตามความต้องการเท่านั้น ซึ่งผลลัพธ์ที่ได้อาจมีความคลาดเคลื่อนเกี่ยวกับค่าของละติจูด และลองจิจูดเล็กน้อยในบางสถานที่ แต่ยังคงถือว่าอยู่ในเกณฑ์ที่รับได้ เมื่อนำไปลงพิกัดจุดไม่คลาดเคลื่อนหรือแตกต่างกันมากนัก อีกทั้งยังช่วยอำนวยความสะดวกให้กับผู้ใช้ได้ระดับหนึ่งที่จะไม่ต้องไปค้นหาพิกัดจากแหล่งข้อมูลอื่นอีก และผู้เขียนหวังเป็นอย่างยิ่งว่า บทความนี้จะเป็นประโยชน์และช่วยอำนวยความสะดวกให้กับการพัฒนาโปรแกรมที่เกี่ยวกับการกำหนดพิกัดจุดบนแผนที่โดยมีการดึงมาจากฐานข้อมูล หรือการพัฒนาโปรแกรมอื่นๆ ที่จำเป็นต้องมีค่าข้อมูลละติจูดลองจิจูดร่วมด้วยไม่มากก็น้อย หากมีส่วนใดของเนื้อหาบทความที่มีความผิดพลาด ผู้เขียนขออภัยไว้ ณ ที่นี้ และหากท่านใดมีข้อมูลเพิ่มเติม หรือพบวิธีการอื่นๆที่สามารถดึงค่าละติจูด-ลองจิจูดนอกเหนือจากวิธีที่กล่าวมาข้างต้นสามารถชี้แนะและแลกเปลี่ยนประสบการณ์ร่วมกันได้นะคะ ขอบคุณค่ะ ^^

แหล่งข้อมูลอ้างอิง :
http://www.aspsnippets.com/Articles/Find-Co-ordinates-Latitude-and-Longitude-of-an-Address-Location-using-Google-Geocoding-API-in-ASPNet-using-C-and-VBNet.aspx
https://developers.google.com/places/javascript/