Using Google map API with Umbraco

Create a Location search form

In part 6 of the series we added functionality to the Location web service that filters the locations that are returned by the service. It achieves this by taking the coordinates (latitude and longitude) of a point on a map and the radius (distance) from that point within which to search for locations.

This final part of the series is about how we can create a web page that queries the web service with the necessary data and consumes its response, displaying any locations that fall within the search area.

The idea behind how this is intended to work is a user can enter the name of a place (or postcode) and choose the distance from that place that they would like to search for locations. When they submit the form having entered a relevant place and distance we use some JQuery/JavaScript to geocode the place (get its latitude and longitude values) and then makes a http JSON request to the Location web service with the latitude, longitude and distance values. Let’s start by creating a new template.

Creating the template

Create a new template called Location search. 

As with previous parts of the series the template code examples use the mark-up for a full HTML page that doesn’t inherit any HTML layout or site CSS from the Umbraco instance that you’re working with. This is so we can avoid any layout or styling issues that could cause problems displaying the map. So let’s add the mark-up for a HTML page:

<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@Umbraco.Field("pageName")</title>
    <script src="~/umbraco/lib/jquery/jquery.min.js"></script>
    <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyC-sOLv-YADP4SK8kJZVFr0j8r2osSs8-k"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
    <style>
        html {
            height:100%;
            overflow-x:hidden;
        }
        body {
            width:100%;
            height:100%;
            margin:0px;
        }
        #search-form {
            padding:20px;
            color:#fff;
            background-color:#000000;
        }
        #map_canvas {
            height:100%;
        }
    </style>
</head>
<body>
<!-- map content and google map JS goes here-->
</body>
</html>

Much the same as before the @Umbraco.Field("pageName") value is used for the page title and that there are two script references: the first is for the local version of JQuery and the second is for the Google map API and includes an API key that I created for this series. You can create your own API key by going to the Google developer’s site and following the instructions. The main difference to previous articles is that I’ve added a CSS link to a Bootstrap CDN and added some in-page styles so that we can style the form that we will add to the template next.

The search form

Add the following form to the body of the html: 

<div id="search-form" class="form-inline">         
    <div class="form-group">             
        <label for="distance">Search for locations within</label>             
        <select id="distance" class="form-control">                 
            <option value="1">1 mile</option>
            <option value="5">5 miles</option>
            <option value="10">10 miles</option>
            <option value="25">25 miles</option>
            <option value="50">50 miles</option>
            <option value="100">100 miles</option>
            <option value="250">250 miles</option>
            <option value="500">500 miles</option>
            <option value="1000">1000 miles</option>
        </select>        
    </div>         
    <div class="form-group">            
        <label for="location">of</label>            
        <input class="form-control" id="location" name="Location" type="text" />
    </div>        
    <div class="form-group">             
        <button type="submit" class="btn btn-default" id="find">Find locations</button>         
    </div>         
    <div class="form-group">             
        <div id="message"></div>         
    </div>     
</div>

The this form is designed to work is that it allows a user to choose a distance from dropdown list and to enter the name of a location in a text field. The dropdown list could easily be a text field as well if you wanted to use a more specific value for the distance. At the end of the form there is an empty div that is used to display any messages that the JS code generates.

Add a map placeholder

Add a div element to the body of the html with an id of ‘map_canvas’ and set its height to 100% so it fills the screen, like so:

<div id="map_canvas" style="height:100%;"></div>

Add the script code 

In a script block add a JQuery click handler that runs on the find locations button click event. 

<script>
    $("#find").click(function () {
        // rest of the code goes in here
    })
</script>

Next, add the code that geocodes the location

var address= $("#location").val();
var distance = $("#distance").val();
var geocoder = new google.maps.Geocoder();

geocoder.geocode({ 'address': address }, function (results, status) {
    if (status == google.maps.GeocoderStatus.OK) {
        var lat = results[0].geometry.location.lat();
        var lng = results[0].geometry.location.lng();
        var location = results[0].formatted_address;
        SearchForLocations(lat, lng, distance, location);          
   } else {
        $("#message").text('Geocode was not successful ' + status);
   }
});

This code first gets the distance and the location values from the form and then uses the google Api geocoder to geocode location value. If the geocode fails then a message is displayed next to the form stating that it wasn’t successful. If it doesn’t fail then it sets variables for the latitude, longitude and location values using the results of the geocode. It then calls a function (SearchForLocations()) passing the latitude, longitude, distance and location as arguments. This is the function that calls the web service.

Next we add the code for the SearchForLocations() function that makes request to web service and creates the map:

function SearchForLocations(lat, lng, distance, location) {
			
    var message = " locations found within " + distance + " miles of " + location;

    $.getJSON("/Umbraco/Api/location/GetLocationsNear?lat=" + lat + "&lng=" + lng + "&radiusInMiles=" + distance, function (locationArray) {
    
        if (!locationArray.length) {
            $("#message").text("No "+ message);
        } else {
            $("#message").text(locationArray.length + message);
        
            var map;
            var marker; 
            var bounds = new google.maps.LatLngBounds();
        
            map = new google.maps.Map(document.getElementById('map_canvas'), { 
                zoom: 8 
            });
        
            for (var i = 0; i < locationArray.length; i++) {
                var location = locationArray[i]; 
                var position = new google.maps.LatLng(location.Geolocation.Latitude, location.Geolocation.Longitude);
            
                bounds.extend(position);
            
                marker = new google.maps.Marker({ 
                    position: position, 
                    map: map, 
                    title: location.Name 
                });
            
                map.fitBounds(bounds); 
             }; 
        }
    }).fail(function () { $("#message").text('Location Api request failed!'); });
}

This code first uses the JQuery getJSON() function to make a request to the web service we created in part 6 of the series. Notice that is passes the latitude, longitude and distance values in the query string to the web service. If the request fails then a relevant message is displayed. If it is successful then the code checks if there are any locations in the array, if not, a relevant message is displayed. If there are locations in the array then a relevant message is displayed and a Google map is created with markers for each of the locations in the array. Thats all there is to it!

The full template code

<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@Umbraco.Field("pageName")</title>
    <script src="~/umbraco/lib/jquery/jquery.min.js"></script>
    <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyC-sOLv-YADP4SK8kJZVFr0j8r2osSs8-k"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
    <style>
        html {
            height:100%;
            overflow-x:hidden;
        }
        body {
            width:100%;
            height:100%;
            margin:0px;
        }
        #search-form {
            padding:20px;
            color:#fff;
            background-color:#000000;
        }
        #map_canvas {
            height:100%;
        }
    </style>
</head>
<body>
<div id="search-form" class="form-inline">         
    <div class="form-group">             
        <label for="distance">Search for locations within</label>             
        <select id="distance" class="form-control">                 
            <option value="1">1 mile</option>
            <option value="5">5 miles</option>
            <option value="10">10 miles</option>
            <option value="25">25 miles</option>
            <option value="50">50 miles</option>
            <option value="100">100 miles</option>
            <option value="250">250 miles</option>
            <option value="500">500 miles</option>
            <option value="1000">1000 miles</option>
        </select>        
    </div>         
    <div class="form-group">            
        <label for="location">of</label>            
        <input class="form-control" id="location" name="Location" type="text" />
    </div>        
    <div class="form-group">             
        <button type="submit" class="btn btn-default" id="find">Find locations</button>         
    </div>         
    <div class="form-group">             
        <div id="message"></div>         
    </div>     
</div>

<div id="map_canvas" style="height:100%;"></div>

<script>
$("#find").click(function () {

    var address= $("#location").val();
    var distance = $("#distance").val();
    var geocoder = new google.maps.Geocoder();

    geocoder.geocode({ 'address': address }, function (results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            var lat = results[0].geometry.location.lat();
            var lng = results[0].geometry.location.lng();
            var location = results[0].formatted_address;
            SearchForLocations(lat, lng, distance, location);          
        } else {
            $("#message").text('Geocode was not successful ' + status);
        }
    });

    function SearchForLocations(lat, lng, distance, location) {
			
        var message = " locations found within " + distance + " miles of " + location;

        $.getJSON("/Umbraco/Api/location/GetLocationsNear?lat=" + lat + "&lng=" + lng + "&radiusInMiles=" + distance, function (locationArray) {
    
            if (!locationArray.length) {
                $("#message").text("No "+ message);
            } else {
                $("#message").text(locationArray.length + message);
        
                var map;
                var marker; 
                var bounds = new google.maps.LatLngBounds();
        
                map = new google.maps.Map(document.getElementById('map_canvas'), { 
                    zoom: 8 
                });
        
                for (var i = 0; i < locationArray.length; i++) {
                    var location = locationArray[i]; 
                    var position = new google.maps.LatLng(location.Geolocation.Latitude, location.Geolocation.Longitude);
            
                    bounds.extend(position);
            
                    marker = new google.maps.Marker({ 
                        position: position, 
                        map: map, 
                        title: location.Name 
                    });
            
                    map.fitBounds(bounds); 
                 }; 
            }
        }).fail(function () { $("#message").text('Location Api request failed!'); });
    }
})
</script>
</body>
</html>

Try it out

To test this template you first of all need to ensure that you've created the web service from part 6 of the series. If that is working then all you need to do to use this template is to set it as the working template for the locations repository content we created in part 3 of the series. 

See the code in action

Comments

To be able to comment you need to login using a Google or Facebook account.