Integrating Google Maps, Places, and Geocode APIs with ASP.NET MVC 5

This article describes the practical approach to integrate Google Maps API, Autocomplete Places API, and Geocode API with the ASP.NET MVC 5 application.

Recently, I was working on a project in which I had to select the venue by applying the autocomplete place API, and on the basis of the venue, I had to get the country, state, city, zip code, latitude, and longitude. If user entered the unknown venue then on the basis of Country and Zip code, state and city would be saved.

To complete the requirement, I have used the following Google APIs.

  • Autocomplete Place API
  • Map API
  • Geocode API

In this article, we will create an ASP.NET MVC application and integrate the above APIs with our web application. So, let’s start the procedure with the following sections,

  • Creating ASP.NET MVC Web Application
  • Working with Application

Creating ASP.NET MVC Application

In this section, we’ll create the ASP.NET Web application with the help of MVC project template in Visual Studio 2013. Follow the steps below.

Step 1. Open the Visual Studio and click on a new project and create a new ASP.Net Web Application named “GooglePlaceMVCSample”.

Creating Web Application

Figure 1: Creating Web Application

Step 2. Select the MVC project template from the next “One ASP.Net wizard”.

MVC Project Template

Figure 2: MVC Project Template

That’s it. Your MVC application is created.

Working with Application

In this section, we will work with the existing “HomeController” and “About.cshtml” page or you can create new controller and view to work with the application. At first we will create a new model so that we can bind the view with the mode and pass some data from the controller to view with the help of model.

Let’s start with the following procedure.

Step 1 Right click on the “Models” folder and add a new class named “Venue”.

Adding class to Model

Figure 3: Adding class to Model

public class Venue
{
    #region Properties

    public int Id { get; set; }

    [Required(ErrorMessage = "Name is required")]
    public string Name { get; set; }

    [Required(ErrorMessage = "ZipCode is required")]
    public string ZipCode { get; set; }

    [Required(ErrorMessage = "Country is required")]
    public string Country { get; set; }

    public string CountryName { get; set; }

    [Required(ErrorMessage = "State is required")]
    public string State { get; set; }

    [Required(ErrorMessage = "City is required")]
    public string City { get; set; }

    public float Latitude { get; set; }

    public float Longitude { get; set; }

    public List<Country> Countries { get; set; }

    public string CountryCode { get; set; }

    #endregion
}

public class Country
{
    #region Properties

    public int CountryId { get; set; }

    public string CountryName { get; set; }

    public string MapReference { get; set; }

    public string CountryCode { get; set; }

    public string CountryCodeLong { get; set; }

    #endregion
}

Step 2. Add a new class as named “CountryModel” and add the following code.

public class CountryModel
{
    #region DatabaseMethod

    public List<T> ConvertTo<T>(DataTable datatable) where T : new()
    {
        List<T> Temp = new List<T>();
        try
        {
            List<string> columnsNames = new List<string>();
            foreach (DataColumn DataColumn in datatable.Columns)
            {
                columnsNames.Add(DataColumn.ColumnName);
            }

            Temp = datatable.AsEnumerable()
                            .ToList()
                            .ConvertAll<T>(row => getObject<T>(row, columnsNames));

            return Temp;
        }
        catch
        {
            return Temp;
        }
    }

    public T getObject<T>(DataRow row, List<string> columnsName) where T : new()
    {
        T obj = new T();
        try
        {
            string columnname = "";
            string value = "";
            PropertyInfo[] Properties = typeof(T).GetProperties();

            foreach (PropertyInfo objProperty in Properties)
            {
                columnname = columnsName.Find(name => name.ToLower() == objProperty.Name.ToLower());
                if (!string.IsNullOrEmpty(columnname))
                {
                    value = row[columnname].ToString();
                    if (!string.IsNullOrEmpty(value))
                    {
                        if (Nullable.GetUnderlyingType(objProperty.PropertyType) != null)
                        {
                            value = row[columnname].ToString().Replace("$", "").Replace(",", "");
                            objProperty.SetValue(obj, Convert.ChangeType(value, Type.GetType(Nullable.GetUnderlyingType(objProperty.PropertyType).ToString())), null);
                        }
                        else
                        {
                            value = row[columnname].ToString();
                            objProperty.SetValue(obj, Convert.ChangeType(value, Type.GetType(objProperty.PropertyType.ToString())), null);
                        }
                    }
                }
            }

            return obj;
        }
        catch
        {
            return obj;
        }
    }

    #endregion

    SqlConnection con;
    SqlDataAdapter adap;
    DataTable dt;
    SqlCommand cmd;

    public CountryModel()
    {
        string conn = ConfigurationManager.ConnectionStrings["CountryConnectionString"].ConnectionString;
        con = new SqlConnection(conn);
    }

    public List<Country> GetCountries()
    {
        List<Country> countries = new List<Country>();
        try
        {
            con.Open();
            adap = new SqlDataAdapter();
            dt = new DataTable();
            cmd = new SqlCommand("GetCountries", con)
            {
                CommandType = CommandType.StoredProcedure
            };

            adap.SelectCommand = cmd;
            adap.Fill(dt);
            countries = ConvertTo<Country>(dt);
        }
        catch (Exception x)
        {
            // Handle exception
        }
        finally
        {
            cmd.Dispose();
            con.Close();
        }

        return countries;
    }
}

Step 3. Open “Web.Config” and add the following connection string code for database,

<add 
    name="CountryConnectionString" 
    connectionString="data source=server name,1436;database=Sample;user id=sa;password=123456;MultipleActiveResultSets=True" 
    providerName="System.Data.SqlClient" />

Note Change the connection string as per your credentials.

Step 4. Open “HomeController” and edit the code with the following highlighted code.

public ActionResult About()
{
    ViewBag.Message = "Your application description page.";
    List<Country> objCountry = new List<Country>();
    CountryModel model = new CountryModel();
    objCountry = model.GetCountries();
    return View(new Venue { Countries = objCountry });
}

Step 5. Now edit the “About.cshtml” page with the following code.

@model GooglePlaceMvcSample.Models.Venue

@{
    ViewBag.Title = "About";
}

<h2>@ViewBag.Title</h2>
<h3>@ViewBag.Message</h3>

<p>Use this area to provide additional information.</p>

<script src="~/Scripts/jquery-2.2.3.js"></script>
<script src="~/Scripts/jquery-2.2.3.min.js"></script>
<script src="~/Scripts/jquery-ui-1.11.4.js"></script>
<script src="~/Scripts/jquery-ui-1.11.4.min.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>

<div class="row">
    <div class="col-md-8">
        <div class="form-group">
            @Html.LabelFor(m => m.Name, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.Name, new { @class = "form-control", id = "TextBoxVenueLocation" })
                @Html.ValidationMessageFor(m => m.Name, "", new { @class = "text-danger" })
                @Html.HiddenFor(m => m.Id, new { @id = "EventVenueId" })
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(m => m.Country, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.DropDownListFor(m => m.CountryCode, new SelectList(Model.Countries, "CountryCode", "CountryName"), "Choose Country", new { @class = "selectBox form-control", @id = "DropDownListCounties" })
                @Html.HiddenFor(m => m.CountryName, new { @id = "HdnCountryName" })
                @Html.ValidationMessageFor(m => m.Country, "", new { @class = "text-danger" })
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(m => m.ZipCode, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.ZipCode, new { @class = "form-control", id = "TextBoxZipCode", maxlength = 6 })
                @Html.ValidationMessageFor(m => m.ZipCode, "", new { @class = "text-danger" })
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(m => m.State, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.State, new { @class = "form-control", id = "TextBoxState" })
                @Html.ValidationMessageFor(m => m.State, "", new { @class = "text-danger" })
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(m => m.City, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.City, new { @class = "form-control", id = "TextBoxCity" })
                @Html.ValidationMessageFor(m => m.City, "", new { @class = "text-danger" })
            </div>
        </div>
        @Html.HiddenFor(m => m.Latitude, new { @id = "EventVenueLatitude" })
        @Html.HiddenFor(m => m.Longitude, new { @id = "EventVenueLongitude" })
    </div>
</div>

Step 6. Open the Content-> Site.css and add the following code in the bottom.

.selectBox {
    width: 295px;
    height: 38px;
    padding: 0 0 0 9px;
}

select option {
    width: 255px;
}

Step 7. Save the application and debug it.

Model Binded Vew in MVC 5

Figure 4: Model Binded Vew in MVC 5

Creating Google Autocomplete Place API Key

At first for accessing the autocomplete place API, you have to create an API Key to access that places API url. To create an API key, follow the steps below.

Step 1. Go to Google Developer Console.

Step 2. Login with your Gmail account and click on Continue to “Create New Project”,

Creating New Project in Google APIs

Figure 5: Creating New Project in Google APIs

Step 3. Enter the name for API Key as shown below,

Create Server API Key

Figure 6: Create Server API Key

Step 4. Now, you can copy your API Key from the next wizard.

API key Info

Figure 7: API key Info

Step 5. Open WebConfig in your project and add a key in the AppSettings tab as shown in the code below,

<add key="GooglePlaceAPIKey" value="Your API Key" />

Note Please change the value with your API Key.

Adding Google Autocomplete Place API

In this section we will apply the Google autocomplete place API. We will return the response in the json format so that it is easy to convert the json result to list. We can also use XML format to get the API result.

Now start with the following steps,

Step 1. At first we will add a class in our model. Add the following code in your Venue class,

public class Prediction
{
    public string description { get; set; }
    public string id { get; set; }
    public string place_id { get; set; }
    public string reference { get; set; }
    public List<string> types { get; set; }
}

public class RootObject
{
    public List<Prediction> predictions { get; set; }
    public string status { get; set; }
}

Step 2. Now just add the following key in your WebConfig file for accessing the place api url.

<add key="GooglePlaceAPIUrl" value="https://maps.googleapis.com/maps/api/place/autocomplete/json?input={0}&types=geocode&language=en&key={1}" />

Step 3. Now create an action method in the HomeController with the help of the following code,

/// <summary>  
/// This method is used to get the place list  
/// </summary>  
/// <param name="SearchText"></param>  
/// <returns></returns>  
[HttpGet, ActionName("GetEventVenuesList")]  
public JsonResult GetEventVenuesList(string SearchText)  
{  
    string placeApiUrl = ConfigurationManager.AppSettings["GooglePlaceAPIUrl"];  
  
    try  
    {  
        placeApiUrl = placeApiUrl.Replace("{0}", SearchText);  
        placeApiUrl = placeApiUrl.Replace("{1}", ConfigurationManager.AppSettings["GooglePlaceAPIKey"]);  
  
        var result = new System.Net.WebClient().DownloadString(placeApiUrl);  
        var Jsonobject = JsonConvert.DeserializeObject<RootObject>(result);  
  
        List<Prediction> list = Jsonobject.predictions;  
  
        return Json(list, JsonRequestBehavior.AllowGet);  
    }  
    catch (Exception ex)  
    {  
        return Json(ex.Message, JsonRequestBehavior.AllowGet);  
    }  
}

Step 4. Now open the “About.cshtml” page and add the following script.

<script type="text/javascript">
    $(function () {
        $("#TextBoxVenueLocation").autocomplete({
            source: function (request, response) {
                $.ajax({
                    url: "@Url.Action("GetEventVenuesList", "Home")",
                    data: { SearchText: request.term },
                    dataType: "json",
                    type: "GET",
                    success: function (data) {
                        if (data.length == 0) {
                            $('#EventVenueId').val("");
                            $('#VenueLocationMesssage').show();
                            return false;
                        } else {
                            response($.map(data, function (item) {
                                return {
                                    label: item.description,
                                    value: item.place_id
                                };
                            }));
                        }
                    },
                    error: function (x, y, z) {
                        alert('error');
                    }
                });
            },
            messages: {
                noResults: "", results: ""
            },
            select: function (event, ui) {
                $('#TextBoxVenueLocation').val(ui.item.label);
                $('#EventVenueId').val(ui.item.value);
                return false;
            }
        }).autocomplete("widget").addClass("CitiesAutocomplete");
    });
</script>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Step 5. Now add the following css code in the Site.css file.

.CitiesAutocomplete {
    list-style: none;
    background: #fbfbfb;
    border: 1px solid #5f5f5f !important;
    width: 350px !important;
    position: absolute;
    z-index: 10000;
    border-top: none;
    min-height: 25px;
    max-height: 250px;
    overflow: auto;
    cursor: pointer;
    padding: 4px 0;
}

.CitiesAutocomplete li {
    padding: 2px 6px;
}

.CitiesAutocomplete li:hover {
    background-color: #9eeffe;
}

Step 6. Now run the application and you will get the location as you type in the Name textbox. As you type in the textbox, the places will be displayed,

Autocomplete Place API Result

Figure 8: Autocomplete Place API Result

We have successfully completed the add Place API. Now, we’ll move to the next section.

Adding Google Map API for Place Details

In this section, we will get all the place details like Country, State, City, Address, Zipcode, Latitude or Longitude etc by using the Google MAP API. We have to pass the PlaceId which will get from the Autocomplete API places. We will get the API result in the XML format and fetch the necessary details from the API result.

Start with the following steps

Step 1. Add the following API url in the webconfig file,

<add 
    key="GooglePlaceDetailsAPIUrl" 
    value="https://maps.googleapis.com/maps/api/place/details/xml?placeid={0}&key={1}" />

Step 2. Add the following methods in the “HomeController”.

/// <summary>
/// This method is used to get place details on the basis of PlaceID
/// </summary>
/// <param name="placeId"></param>
/// <returns></returns>
[HttpGet, ActionName("GetVenueDetailsByPlace")]
public JsonResult GetVenueDetailsByPlace(string placeId)
{
    string placeDetailsApi = ConfigurationManager.AppSettings["GooglePlaceDetailsAPIUrl"];
    try
    {
        Venue ven = new Venue();
        placeDetailsApi = placeDetailsApi.Replace("{0}", placeId);
        placeDetailsApi = placeDetailsApi.Replace("{1}", ConfigurationManager.AppSettings["GooglePlaceAPIKey"]);

        var result = new System.Net.WebClient().DownloadString(placeDetailsApi);

        var xmlElm = XElement.Parse(result);
        ven = GetAllVenueDetails(xmlElm);

        return Json(ven, JsonRequestBehavior.AllowGet);
    }
    catch (Exception ex)
    {
        return Json(ex.Message, JsonRequestBehavior.AllowGet);
    }
}

/// <summary>
/// This method is created to get the place details from xml
/// </summary>
/// <param name="xmlElm"></param>
/// <returns></returns>
private Venue GetAllVenueDetails(XElement xmlElm)
{
    Venue ven = new Venue();
    List<string> c = new List<string>();
    List<string> d = new List<string>();

    // Get the status of download xml file
    var status = (from elm in xmlElm.Descendants()
                  where elm.Name == "status"
                  select elm).FirstOrDefault();

    // If download xml file status is ok
    if (status.Value.ToLower() == "ok")
    {
        // Get the address_component element
        var addressResult = (from elm in xmlElm.Descendants()
                             where elm.Name == "address_component"
                             select elm);

        // Get the location element
        var locationResult = (from elm in xmlElm.Descendants()
                              where elm.Name == "location"
                              select elm);

        foreach (XElement item in locationResult)
        {
            ven.Latitude = float.Parse(item.Elements().Where(e => e.Name.LocalName == "lat").FirstOrDefault().Value);
            ven.Longitude = float.Parse(item.Elements().Where(e => e.Name.LocalName == "lng").FirstOrDefault().Value);
        }

        // Loop through for each address_component
        foreach (XElement element in addressResult)
        {
            string type = element.Elements().Where(e => e.Name.LocalName == "type").FirstOrDefault().Value;

            if (type.ToLower().Trim() == "locality") // If type is locality then get the locality
            {
                ven.City = element.Elements().Where(e => e.Name.LocalName == "long_name").Single().Value;
            }
            else
            {
                if (type.ToLower().Trim() == "administrative_area_level_2" && string.IsNullOrEmpty(ven.City))
                {
                    ven.City = element.Elements().Where(e => e.Name.LocalName == "long_name").Single().Value;
                }
            }

            if (type.ToLower().Trim() == "administrative_area_level_1") // If type is administrative_area_level_1 then get the state
            {
                ven.State = element.Elements().Where(e => e.Name.LocalName == "long_name").Single().Value;
            }

            if (type.ToLower().Trim() == "country") // If type is country then get the country
            {
                ven.Country = element.Elements().Where(e => e.Name.LocalName == "long_name").Single().Value;
                ven.CountryCode = element.Elements().Where(e => e.Name.LocalName == "short_name").Single().Value;
                if (ven.Country == "United States") { ven.Country = "USA"; }
            }

            if (type.ToLower().Trim() == "postal_code") // If type is postal_code then get the postal code
            {
                ven.ZipCode = element.Elements().Where(e => e.Name.LocalName == "long_name").Single().Value;
            }
        }
    }
    xmlElm.RemoveAll();
    return ven;
}

Note. In the above code, there are two methods, the first one is used to call the api and get the api result in the xml format. Second method is used to get all the required place details like Country, State, City, Latitude and Longitude. You can fetch the details from the json result also.

Step 3. In the “About.cshtml” page, update the autocomplete method with the help of the highlighted code below.

Change the select method in autocomplete.

select: function (event, ui) {
    $('#TextBoxVenueLocation').val(ui.item.label);
    $('#EventVenueId').val(ui.item.value);
    GetVenueDetailsByPlaceId(ui.item.value);
    return false;
}

Add the following function in the script.

function GetVenueDetailsByPlaceId(PlaceId) {
    $.ajax({
        url: '@Url.Action("GetVenueDetailsByPlace", "home")',
        dataType: "json",
        data: {
            placeId: PlaceId
        },
        type: "GET",
        async: false,
        error: function (xhr, status, error) {
            var err = eval("(" + xhr.responseText + ")");
            toastr.error(err.message);
        },
        success: function (data) {
            $('#TextBoxZipCode').val(data.ZipCode);
            $('#DropDownListCounties option:selected').text(data.Country);
            $('#TextBoxState').val(data.State);
            $('#TextBoxCity').val(data.City);
            $('#EventVenueLatitude').val(data.Latitude);
            $('#EventVenueLongitude').val(data.Longitude);
        },
        beforeSend: function () {
            $("#divProcessing").show();
        }
    });
}

Step 4. Now with the help of this code, we will fetch the place details and display the details in the textbox. Run the application and search for any genuine place.

Searching Place using Autocomplete API

Figure 9: Searching Place using Autocomplete API

Now select the place,

Place Details using MAP Place API

Figure 10: Place Details using MAP Place API

That’s it with this section. Now we’ll move to further sections.

Adding Google Geocode API

In this section, we’ll use the Google Geocode API to get the place details in case there are no matching records displayed in the autocomplete API result. So in that case, we can get the place details by providing the Zip code and country as a region in the api.

Let’s start with the following steps.

Step 1. Add the following API url in the Web.Config file.

<add key="GoogleGeocodeAPIUrl" value="https://maps.googleapis.com/maps/api/geocode/xml?address={0}&region={1}&key={2}" />

Step 2. Add the following method in the “HomeController”.

/// <summary>  
/// This method is created to get the place details on the basis of zip code and country  
/// </summary>  
/// <param name="zipCode"></param>  
/// <param name="region"></param>  
/// <returns></returns>  
[HttpGet, ActionName("GetVenueDetailsByZipCode")]  
public JsonResult GetVenueDetailsByZipCode(string zipCode, string region)  
{  
    string geocodeApi = ConfigurationManager.AppSettings["GoogleGeocodeAPIUrl"];  
    try  
    {  
        Venue ven = new Venue();  
        geocodeApi = geocodeApi.Replace("{0}", zipCode);  
        geocodeApi = geocodeApi.Replace("{1}", region);  
        geocodeApi = geocodeApi.Replace("{2}", ConfigurationManager.AppSettings["GooglePlaceAPIKey"]);  

        var result = new System.Net.WebClient().DownloadString(geocodeApi);  

        var xmlElm = XElement.Parse(result);  
        ven = GetAllVenueDetails(xmlElm);  
        return Json(ven, JsonRequestBehavior.AllowGet);  
    }  
    catch (Exception ex)  
    {  
        return Json(ex.Message, JsonRequestBehavior.AllowGet);  
    }  
}

Step 3. Now add the following function in the “About.cshtml” page.

/* This method is used to accept only numeric. */
$("#TextBoxZipCode").keydown(function (e) {
    // Allow: backspace, delete, tab, escape, enter and .
    if ($.inArray(e.keyCode, [46, 8, 9, 27, 13, 110, 190]) !== -1 ||
        // Allow: Ctrl+A, Ctrl+C, Ctrl+V, Command+A
        ((e.keyCode == 65 || e.keyCode == 86 || e.keyCode == 67) && (e.ctrlKey === true || e.metaKey === true)) ||
        // Allow: home, end, left, right, down, up
        (e.keyCode >= 35 && e.keyCode <= 40)) {
        // let it happen, don't do anything
        return;
    }
    // Ensure that it is a number and stop the keypress
    if ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) {
        $("#TextBoxZipCode").val('');
        e.preventDefault();
        return false;
    }
});

var timer;
$("#TextBoxZipCode").on('keyup', function (event) {
    clearTimeout(timer);  // clear any running timeout on key up
    timer = setTimeout(function () {  // then give it a second to see if the user is finished
        if ($('#DropDownListCounties option:selected').val() == '' && $("#TextBoxZipCode").val() != '') {
            alert('Country is required');
            $("#TextBoxZipCode").val('');
            return false;
        } else {
            $.ajax({
                url: '@Url.Action("GetVenueDetailsByZipCode", "home")',
                dataType: "json",
                data: {
                    zipCode: $("#TextBoxZipCode").val(),
                    region: $("#DropDownListCounties").val()
                },
                type: "GET",
                async: false,
                error: function (xhr, status, error) {
                    var err = eval("(" + xhr.responseText + ")");
                    toastr.error(err.message);
                },
                success: function (data) {
                    $('#TextBoxZipCode').val(data.ZipCode);
                    $('#TextBoxState').val(data.State);
                    $('#TextBoxCity').val(data.City);
                    $('#EventVenueLatitude').val(data.Latitude);
                    $('#EventVenueLongitude').val(data.Longitude);
                },
                beforeSend: function () {
                    $("#divProcessing").show();
                }
            });
        }
    }, 500);
});

Note. With the help of the above code, the user can only enter numbers in the zip code textbox and after entering the number, the API result will be displayed. I have made the country required because in many cities zip codes arethe same so in that case, region (country) will be helpful to get the particular result.

Note about API KEY

If your API key will not get the result or there is some error generated to get the result then you go here and create the new API key for Geocode API.

Step 4. Now run the application and search the place, if the place is not listed in the api result, just select the country at first and then enter the zip code and you will get the result from the Geocode API.

Place Details using Geocode API

Figure 11: Place Details using Geocode API

That’s it for this article.

Summary

This article described how we can use different Google provided APIs and how to integrate Google APIs in the ASP.Net MVC Application.

 


Similar Articles