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.
Select your template as empty and check "Use HTML semantic markup".
Getting SignalR
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.
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.
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
.
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",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
Create a controller named ‘HomeController
’:
Then create a default index view for that controller:
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.
@{
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');
});
function SetUpMap(centerLocation) {
var mapOptions = {
zoom: 4,
center: centerLocation,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("divMapDiv"), mapOptions);
}
function GeoCodeLocation(location, type) {
geoCoder.geocode({ 'address': location }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (type == 'initial') {
SetUpMap(results[0].geometry.location);
}
else if (type == 'setPoint') {
BroadCastLocation(results[0].geometry.location);
}
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
function BroadCastLocation(location) {
if (connection != null) {
var LatLngAsString = '{"lat":' + location.lat() + ',"lng":' + location.lng() + '}';
connection.send(LatLngAsString);
}
}
function SetMarker(location) {
if (map != null) {
var marker = new google.maps.Marker({
map: map,
position: location,
animation: google.maps.Animation.DROP
});
}
}
function SetUpSignalR() {
connection = $.connection('/echo');
connection.received(function (data) {
var dynamicLocationObject = jQuery.parseJSON(data);
var latAndLonObject = new google.maps.LatLng(dynamicLocationObject.lat, dynamicLocationObject.lng);
SetMarker(latAndLonObject);
});
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.