Jan 18, 2014

Getting and Displaying The Nearest N Places On a Map in ASP.NET

In my previous tutorial, we implemented to save Geolocation (latitude, longitude) in Geography data type column in Sql Server. Now, we will get the nearest N places from the current geo-location of user.

Environment: VS2012,.NET Framework 4.5, ASP.NET Empty Website template, C#, SQL Server v11.0, Entity Framework 5.0 Database First approach.

HTML Structure:

Add a new Web Form and reference jQuery in head tag


  <script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.js"></script>
 

Add following code


  <form id="form1" runat="server">
        <p id="message"></p>
        <asp:HiddenField ID="hdnLocation" runat="server" />
        <asp:Button ID="btnSubmit" runat="server" Text="Get 5 Nearest Places" OnClick="btnSubmit_Click" />
        <asp:GridView ID="GridView1" runat="server" CellPadding="3"></asp:GridView>
        <div id="map" style="width: 600px; height: 400px;"></div>
    </form>
 

Hidden field is used to save current user geographical position similar to previous post. On submit, Gridview and Map will display the nearest 5 places.

To Get Current User Geo-Location:

We use same HTML5 Geo-Location API to get current user geographical position.


   $('[id*=btnSubmit]').prop('disabled', true);

        var currentLatLng;
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(showPosition, showError);
        }
        else { $("#message").html("Geolocation is not supported by this browser."); }

        function showPosition(position) {
            currentLatLng = position.coords;
            
            var latlon = "Latitude" + currentLatLng.latitude + "," + "Longitude" + currentLatLng.longitude;
            $("#message").html(latlon);
            $("[id*=hdnLocation]").val(currentLatLng.longitude + " " + currentLatLng.latitude);
            $('[id*=btnSubmit]').prop('disabled',false);
        }

        function showError(error) {
            if (error.code == 1) {
                $("#message").html("User denied the request for Geolocation.");
            }
            else if (error.code == 2) {
                $("#message").html("Location information is unavailable.");
            }
            else if (error.code == 3) {
                $("#message").html("The request to get user location timed out.");
            }
            else {
                $("#message").html("An unknown error occurred.");
            }
        }
 

By default, submit button is disabled. It is enabled on getting current user geo-location.

Getting The Nearest Places:

First, I would recommend to read my previous post to get idea about database structure. I am using same scene here. The following code is to get the nearest 5 places:


  var currentLocation = DbGeography.FromText("POINT(" + hdnLocation.Value + ")"); 
        string[] latlng = hdnLocation.Value.Split(new char[]{' '}); 
        using ( var context = new SampleDBEntities()){

            var places = (from u in context.PlaceInfoes
                          orderby u.Geolocation.Distance(currentLocation)
                          select u).Take(5).Select(x => new { Name = x.Name, Address = x.Address, City = x.City, State = x.State, Latitude = x.Geolocation.Latitude, Longitude = x.Geolocation.Longitude });

 

Entity Framework 5 and above supports spatial type operations. Here we are doing order by distance from current location and take top 5 records in LINQ. EF5+ makes it very easy.

To Display Data In GridView:


  //Bind GridView
            GridView1.DataSource = places.ToList();
            GridView1.DataBind();

To Display Data In Map:

We serialize and assign marker points in javascript variable points from server side and on client side, this variable will be used to display markers


 //Set points for map
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            var output = serializer.Serialize(places);
            ClientScript.RegisterClientScriptBlock(GetType(), "points", "var points = " + output + ";var currentLoc = { 'Latitude' : " + latlng[1] + ", 'Longitude':"+latlng[0] +" }", true);
        }

In above code, currentLoc variable is used to hold current geo-location of user.

on client side:


 var map2, infoWindow;

        if (typeof points !== "undefined") {
            var mapOptions = {
                center: new google.maps.LatLng(points[0].Latitude, points[0].Longitude),
                zoom: 9,
                mapTypeId: google.maps.MapTypeId.ROADMAP
            };
            infoWindow = new google.maps.InfoWindow();
            map2 = new google.maps.Map(document.getElementById("map"), mapOptions);

            //marker for current location
            var marker = new google.maps.Marker({
                position: new google.maps.LatLng(currentLoc.Latitude, currentLoc.Longitude),
                map: map2,
                icon: 'https://maps.google.com/mapfiles/kml/shapes/schools_maps.png'
            });

            //marker for nearest places
            for (i = 0; i < points.length; i++) {
                var data = points[i]
                var myLatlng = new google.maps.LatLng(data.Latitude, data.Longitude);
                var marker2 = new google.maps.Marker({
                    position: myLatlng,
                    map: map2,
                    title: data.Name
                });
                (function (marker2, data) {
                    google.maps.event.addListener(marker2, "click", function (e) {
                        infoWindow.setContent(data.Name);
                        infoWindow.open(map2, marker2);
                    });
                })(marker2, data);
            }
        }

In above javascript code, current location is defined with different marker icon.

Output:

nearest places geolocation

Conclusion:

We saw the application of Sql server Geography data type and good support of Entity framework which makes few lines of code to get the nearest N places done.

Enjoy EF !!