Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Dynamic Two Way Real Time Map – Using SignalR and Google Maps

4.67/5 (3 votes)
27 Feb 2013CPOL4 min read 31.6K   703  
An example of two way communication between a web application and its connected users

Introduction

The goal of this article will be to show an example of two way communication between a web application and its connected users. We will make a web page containing a map which will dynamically update itself in real time for any user viewing the page when a user submits a location to it.

Google Maps

Google Maps is a web mapping service application. It provides users with services such as creating visual maps and services such as geo-coding. Luckily for us, they also provide a very friendly and well documented API for developer use.

For this tutorial, you will need to be able to access Google’s Mapping API and other related services. This link will guide you through the process of creating a Google API developer account and how to turn on access to the Map API. When you are done with this process, you should have access to what is known as your API "key". This key will be used later in the tutorial to make service calls to the JavaScript API.
Note: There are some usage restrictions that are applied with the free use of Google’s APIs. Review the details and user agreement carefully.

SignalR

SignalR is an open source code library that allows us to enable real-time two communication for .NET applications. The high point of this library is its ability to decide the best communication method "behind the scenes" for the developer. From what I understand, the methods used to do this are HTML Websockets or if not available, then Long Polling.

Note: Read the documentation for SignalR and for whatever browsers you or your users may use for information that may cause issues with performing the actions in this tutorial.

Let’s Get Started

First, start off by creating a new MVC3 project.

CreateProject

Select your template as empty and check "Use HTML semantic markup".

SelectTemplate

Getting SignalR

NuGetPackage2

Use the Package Manager Console and run the above command. This should get the necessary libraries to get you started. If you don’t see the package console manager, go here for more help or go to SignalR’s GitHub page and get it.

References

If everything went OK, you should have a nice new batch of SignalR related libraries added to your references.

Setting SignalR Up

First, create a file called MyPersistentConnection.cs using the following code. As the SignalR documentation tells us. First, we have to set a class that will be our endpoint for persistent connections to the server. We override the OnReceived function. This function controls what the server will do when it receives a message from the client. Here, we will just broadcast the message data to all clients.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;

namespace TwoWayMap.Website
{
    public class MyPersistentConnection : PersistentConnection
    {
        protected override Task OnReceived(IRequest request, string connectionId, string data)
        {
            return Connection.Broadcast(data);
        }
    }
}

Next, modify the Global.asax.cs file as so. In the RegisterRoutes function, we have added a route name "echo" that is mapped to our endpoint that derives from PersistentConnection.

C#
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
}

public static void RegisterRoutes(RouteCollection routes)
{
     routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.MapConnection<MyPersistentConnection>("echo", "/echo");

    routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
    );
}

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    // Use LocalDB for Entity Framework by default
    //Database.DefaultConnectionFactory = new SqlConnectionFactory(
    //    @"Data Source=(localdb)\v11.0; Integrated Security=True; MultipleActiveResultSets=True");

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
}

Create a controller named ‘HomeController’:

CreateHomeController2

Then create a default index view for that controller:

CreateIndexView

By the way, you may have to modify your _Layout.cshtml file to reflect the changes that the Nuget pull of SignalR may have done to your default Jquery file. It may have updated it to a different version and your layout file will have to reflect this for everything to work correctly.

This is the code we will place in our Index.cshtml view file. I tried to include some good explanatory comments, but I’ll give a brief overview here. First, we setup the SignalR client stuff by creating a connection object that uses the route of the endpoint we created as a parameter. For the button’s click event, we get the value from the textbox and then attempt to get a geo-location from it using Google’s geocode service. If the service successfully gets a geocode location, we use its information as what we send to the SignalR endpoint using our connection object. When data is received from the connection object, we parse it a dynamic JSON object and use its latitude and longitude values to create a new Google location object. Finally, we take that information and create a place marker on the map. Therefore, we update the maps of every user connected to this page.

HTML
@{
ViewBag.Title = "Map Tutorial";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<style type="text/css">
#divMapDiv
{
width: 800px;
height: 800px;
}
</style>
<script src="Scripts/jquery.signalR-1.0.0.min.js" type="text/javascript"></script>
<script src="http://maps.googleapis.com/maps/api/js?key=YOUR_GOOGLE_API_KEY_HERE&sensor=false"
type="text/javascript"></script>
<script type="text/javascript">
var geoCoder;
var map;
var connection;
$(document).ready(function () {
SetUpSignalR();
geoCoder = new google.maps.Geocoder();
GeoCodeLocation('Nashville, TN', 'initial'); //change this to wherever 
                                             //you want your mapped centered to
});

//this function sets up our map using the passed in location as the center
function SetUpMap(centerLocation) {
var mapOptions = {
zoom: 4,
center: centerLocation,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
//this uses the divMapDiv element as the container to the map, change it to whatever you want
map = new google.maps.Map(document.getElementById("divMapDiv"), mapOptions);
}

//this function will attempt to make a call to the Google geocoding using the location 
//from the textbox and the type of call
function GeoCodeLocation(location, type) {
geoCoder.geocode({ 'address': location }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
//if succefful and this is the initial call we will use the coordinates from this 
//to create a center point for our map
if (type == 'initial') {
SetUpMap(results[0].geometry.location);
}
else if (type == 'setPoint') {
//if not initial call, then we will use the location object 
//as what we will send to the server to broadcast to other users
BroadCastLocation(results[0].geometry.location);
}
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}

//this function will broadcast the received location object to the other clients
function BroadCastLocation(location) {
if (connection != null) {
var LatLngAsString = '{"lat":' + location.lat() + ',"lng":' + location.lng() + '}';
connection.send(LatLngAsString);
}
}

//this function will take a location object and create a marker instantly on our Google map 
//at the given location
function SetMarker(location) {
if (map != null) {
var marker = new google.maps.Marker({
map: map,
position: location,
animation: google.maps.Animation.DROP
});
}
}

//set up for the client side SignalR stuff
function SetUpSignalR() {
//create our connection object from the endpoint we set up on the 'echo' route. 
//We will send messages and receive them from here.
connection = $.connection('/echo');

//every time we get information back from SignalR, we will assume its location data as a JSON object
//we will then parse it and create a Google geocode location object
//then use that to set our marker
connection.received(function (data) {
var dynamicLocationObject = jQuery.parseJSON(data);
var latAndLonObject = new google.maps.LatLng(dynamicLocationObject.lat, dynamicLocationObject.lng);
SetMarker(latAndLonObject);
});

//after we make a successful connection, we will make the button's function 
//take the value from the textbox and attempt to geocode it and broadcast it to the other users
connection.start().done(function () {
$("#btnSetMarker").click(function () {
var locationValue = $('#txtLocation').val();
if (locationValue != null &&
locationValue != '') {
GeoCodeLocation(locationValue, 'setPoint');
}
});
});
}
</script>
<h2>
Google Maps and Signal R Tutorial</h2>
Location:<input type="text" id="txtLocation" />
<br />
<input type="button" value="Test" id="btnSetMarker" />
<div id="divMapDiv">
</div>

Give It A Go

When you fire your project up, you should get a page with a blank map centered at your initial position. To test, type in a location (city, address, state, country) and a marker should be placed at that location if it’s valid.

Now the real test is when you open another browser and type in a location and both maps are updated!! Next run the site on a server and get others to start all entering locations and watch the map get updated with locations from other people.

A couple of points:

  • You have to make sure that you put your Google API that you got as described above in the querystring in the script tag’s src attribute where I have put a place marker that says "YOUR_GOOGLE_API_KEY_HERE".
  • Check Google’s API documentation for good getting started tutorials.
  • Also as you test, remain conscious of the limit imposed by the free use of the Google API.

Conclusion

In conclusion, we see just how powerful and easy it is to get two communication between your web app and its users using the SignalR library. The possibilities for what can be achieved are endless.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)