|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionI often looked at the maps provided by my web analytic software and thought how cool it would be if I could provide these types of maps for the users currently browsing my web site, in real time. So my quest began to add this lush novelty item to my personal web site. I found the virtual maps JavaScript API and hostip.info API, and with a little bit more work and research, I had it. The tutorial below describes how you can implement a modal popup window on your web site which will display a Microsoft Virtual Earth map with pinpoints on the locations of everyone who is currently browsing your web site. What you will need to get started
Using the codeI have broken the code you will need to implement, down into five steps below: Step 1: A UserIpInfo singleton collection.This is used to store session information for each user at the application level; this is necessary so that each user will have access to view the information of others. Alternatively, web.caching or an application variable could be used in place of a Singleton collection, but those would cause the objects to be serialized/deserialized when accessed. Since this is a novelty item, you want to make sure the addition does not hurt the performance of your website. VB.NET''' <summary />
''' UserIPInfoList
''' </summary />
''' <remarks /></remarks />
Public Class UserIPInfoList
Inherits Generic.Dictionary(Of String, UserIpInfo)
''' <summary />
''' Instance
''' </summary />
''' <remarks />
''' The current running instance.
''' </remarks />
Private Shared m_instance As UserIPInfoList
Public Shared ReadOnly Property Instance() As UserIPInfoList
Get
' initialize if not already done
If m_instance Is Nothing Then
m_instance = New UserIPInfoList
End If
' return the initialized instance of the Singleton Class
Return m_instance
End Get
End Property
''' <summary />
''' New
''' </summary />
''' <remarks /></remarks />
Private Sub New()
MyBase.New()
End Sub
End Class
''' <summary />
''' UserIpInfo
''' </summary />
''' <remarks /></remarks />
Public Class UserIpInfo
''' <summary />
''' IPAddress
''' </summary />
''' <remarks /></remarks />
Private m_IPAddress As String
Public Property IPAddress() As String
Get
Return m_IPAddress
End Get
Set(ByVal value As String)
m_IPAddress = value
End Set
End Property
''' <summary />
''' Location
''' </summary />
''' <remarks /></remarks />
Private m_Location As String
Public Property Location() As String
Get
Return m_Location
End Get
Set(ByVal value As String)
m_Location = value
End Set
End Property
''' <summary />
''' Coordinates
''' </summary />
''' <remarks /></remarks />
Private m_Coordinates As String
Public Property Coordinates() As String
Get
Return m_Coordinates
End Get
Set(ByVal value As String)
m_Coordinates = value
End Set
End Property
''' <summary />
''' CoordinatesReversed
''' </summary />
''' <value /></value />
''' <returns /></returns />
''' <remarks /></remarks />
Public ReadOnly Property CoordinatesReversed()
Get
Dim corray As String() = m_Coordinates.Split(",")
Return corray(1) & "," & corray(0)
End Get
End Property
''' <summary />
''' New
''' </summary />
''' <param name="iPAddress" /></param />
''' <param name="location" /></param />
''' <param name="coordinates" /></param />
''' <remarks /></remarks />
Public Sub New(ByVal iPAddress As String, _
ByVal location As String, _
ByVal coordinates As String)
Me.IPAddress = iPAddress
Me.Location = location
Me.Coordinates = coordinates
End Sub
''' <summary />
''' New
''' </summary />
''' <remarks /></remarks />
Public Sub New()
End Sub
End Class
C#/// <summary>
/// UserIPInfoList
/// </summary>
/// <remarks></remarks>
public class UserIPInfoList : Generic.Dictionary<string, UserIpInfo>
{
/// <summary>
/// Instance
/// </summary>
/// <remarks>
/// The current running instance.
/// </remarks>
private static UserIPInfoList m_instance;
public static UserIPInfoList Instance {
get {
// initialize if not already done
if (m_instance == null) {
m_instance = new UserIPInfoList();
}
// return the initialized instance of the Singleton Class
return m_instance;
}
}
/// <summary>
/// New
/// </summary>
/// <remarks></remarks>
private UserIPInfoList() : base()
{
}
}
/// <summary>
/// UserIpInfo
/// </summary>
/// <remarks></remarks>
public class UserIpInfo
{
/// <summary>
/// IPAddress
/// </summary>
/// <remarks></remarks>
private string m_IPAddress;
public string IPAddress {
get { return m_IPAddress; }
set { m_IPAddress = value; }
}
/// <summary>
/// Location
/// </summary>
/// <remarks></remarks>
private string m_Location;
public string Location {
get { return m_Location; }
set { m_Location = value; }
}
/// <summary>
/// Coordinates
/// </summary>
/// <remarks></remarks>
private string m_Coordinates;
public string Coordinates {
get { return m_Coordinates; }
set { m_Coordinates = value; }
}
/// <summary>
/// CoordinatesReversed
/// </summary>
/// <value></value>
/// <returns></returns>
/// <remarks></remarks>
public object CoordinatesReversed {
get {
string[] corray = m_Coordinates.Split(",");
return corray(1) + "," + corray(0);
}
}
/// <summary>
/// New
/// </summary>
/// <param name="iPAddress"></param>
/// <param name="location"></param>
/// <param name="coordinates"></param>
/// <remarks></remarks>
public UserIpInfo(string iPAddress, string location, string coordinates)
{
this.IPAddress = iPAddress;
this.Location = location;
this.Coordinates = coordinates;
}
/// <summary>
/// New
/// </summary>
/// <remarks></remarks>
public UserIpInfo()
{
}
}
Step 2: Gathering user information in your Global.asaxYou can use the global.asax file to gather information about your user on session start, and remove it on session end. The code block below will get the user's IP address, look up the location of that address, and store it in your singleton collection with the session ID as the key. When the user session ends, it will be removed based on the unique session ID. VB.NETSub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
' Fires when the session is started
Application.Lock()
'add session ip address to list.
If Session.IsNewSession() AndAlso Not _
UserIPInfoList.Instance.ContainsKey(Session.SessionID) Then
Try
'make a call to hostip handler to get ip info
Dim hostIPLookupXML As New XmlDocument
hostIPLookupXML.Load("http://api.hostip.info/?ip=" & _
Request.UserHostAddress)
'uncomment to test.
'hostIPLookupXML.Load("http://api.hostip.info/?ip=12.215.42.19")
'add namespace
Dim nsMgr As New XmlNamespaceManager(hostIPLookupXML.NameTable)
nsMgr.AddNamespace("gml", "http://www.opengis.net/gml")
'select node
Dim Hostip As XmlNode = _
hostIPLookupXML.DocumentElement.SelectSingleNode("gml:featureMember", _
nsMgr).FirstChild
'check for bad results.
If Hostip IsNot Nothing Then
'parse data to local vars.
Dim locationCityState As String = Hostip.ChildNodes(0).InnerText
Dim locationCountry As String = Hostip.ChildNodes(1).InnerText
If Hostip.ChildNodes.Count > 3 AndAlso _
Hostip.ChildNodes(4) IsNot Nothing Then
'check that we have cooridinates.
Dim coordinates As String = Hostip.ChildNodes(4).InnerText
If coordinates <> String.Empty Then
'add user info to list.
UserIPInfoList.Instance.Add(Session.SessionID, _
New UserIpInfo(Request.UserHostAddress, _
locationCityState & " " & locationCountry, _
coordinates))
End If
End If
End If
Catch ex As Exception
'service is unavialable, ignore error.
End Try
End If
Application.UnLock()
End Sub
Sub Session_End(ByVal sender As Object, ByVal e As EventArgs)
' Fires when the session ends
Application.Lock()
Try
Dim ipi As New BusinessObjects.ProgrammersJournal.UserIpInfo
If UserIPInfoList.Instance.TryGetValue(Session.SessionID, ipi) Then _
UserIPInfoList.Instance.Remove(Session.SessionID)
Catch ex As Exception
'move on
End Try
Application.UnLock()
End Sub
C#public void Session_Start(object sender, EventArgs e)
{
// Fires when the session is started
Application.Lock();
//add session ip address to list.
if (Session.IsNewSession() && _
!UserIPInfoList.Instance.ContainsKey(Session.SessionID)) {
try {
//make a call to hostip handler to get ip info
XmlDocument hostIPLookupXML = new XmlDocument();
hostIPLookupXML.Load("http://api.hostip.info/?ip=" + _
Request.UserHostAddress);
//uncomment to test.
//hostIPLookupXML.Load("http://api.hostip.info/?ip=12.215.42.19")
//add namespace
XmlNamespaceManager nsMgr = new XmlNamespaceManager(hostIPLookupXML.NameTable);
nsMgr.AddNamespace("gml", "http://www.opengis.net/gml");
//select node
XmlNode Hostip = _
hostIPLookupXML.DocumentElement.SelectSingleNode("gml:featureMember", _
nsMgr).FirstChild;
//check for bad results.
if (Hostip != null) {
//parse data to local vars.
string locationCityState = Hostip.ChildNodes(0).InnerText;
string locationCountry = Hostip.ChildNodes(1).InnerText;
if (Hostip.ChildNodes.Count > 3 && Hostip.ChildNodes(4) != null) {
//check that we have cooridinates.
string coordinates = Hostip.ChildNodes(4).InnerText;
if (coordinates != string.Empty) {
//add user info to list.
UserIPInfoList.Instance.Add(Session.SessionID,
new UserIpInfo(Request.UserHostAddress,
locationCityState + " " + locationCountry, coordinates));
}
}
}
}
catch (Exception ex) {
//service is unavialable, ignore error.
}
}
Application.UnLock();
}
public void Session_End(object sender, EventArgs e)
{
// Fires when the session ends
Application.Lock();
try {
BusinessObjects.ProgrammersJournal.UserIpInfo ipi =
new BusinessObjects.ProgrammersJournal.UserIpInfo();
if (UserIPInfoList.Instance.TryGetValue(Session.SessionID, ipi))
UserIPInfoList.Instance.Remove(Session.SessionID);
}
catch (Exception ex) {
//move on
}
Application.UnLock();
}
Step 3: Placing the map on your page in a modal popupThe next step is to simply place the map on your page in a modal pop up window. You will need to include a reference to the JavaScript API on your page, create a function for loading the map, create a modal pop up window, and add your JavaScript function to the body <!-- Add link to jscript api -->
<script src="http://dev.virtualearth.net/mapcontrol/v3/mapcontrol.js">
</script>
<!--add script to load map-->
<script type="text/javascript">
//<![CDATA[
var map = null;
function GetMap()
{
//display map
map = new VEMap('myMap');
map.LoadMap(new VELatLong(115,-150),1,'r',false);
//hide dashboard.
map.HideDashboard();
}
//]]>
</script>
<!--call load map script when page loads-->
<body onload="GetMap();">
<!--Add link button to display modal window-->
<asp:LinkButton ID="LinkButton2" runat="server" ToolTip="whats this?">
<asp:Literal ID="numonline" runat="server"></asp:Literal>
users online
</asp:LinkButton>
<!--add panel to hold map-->
<asp:Panel ID="Panel2" runat="server" Style="display: none" CssClass="modalPopup">
<asp:Panel ID="Panel4" runat="server"
Style="cursor: move; background-color: #a5c863; padding: 3px;
border: solid 1px Gray; color: white; margin-bottom: 3px;">
<div>
<strong>Whos Online?</strong>
</div>
</asp:Panel>
<div id="myMap" style="position: relative; width: 400px; height: 200px;">
</div>
<br />
<asp:Button ID="OkButton" runat="server" Text="done" CssClass="loginbox" />
</asp:Panel>
<!--add modal extender-->
<cc1:ModalPopupExtender ID="ModalPopupExtender1"
runat="server" TargetControlID="LinkButton2"
PopupControlID="Panel2" BackgroundCssClass="modalBackground"
OkControlID="OkButton" DropShadow="true"
Y="35" PopupDragHandleControlID="Panel4" />
Step 4: Adding pinpoints to your mapThe next step is to loop through your singleton collection and add the pinpoints to your map. We will do this by dynamically creating the JavaScript in the code-behind page. You will then need to call your new function from the body VB.NET'set number of users online.
Me.numonline.Text = UserIPInfoList.Instance.Keys.Count
'add pinpoints to map.
Dim sb As New StringBuilder()
Dim count As Integer = 1
sb.AppendLine("function ShowPins()")
sb.AppendLine("{")
For Each key As String In UserIPInfoList.Instance.Keys
sb.AppendLine("var pinID = " & count & ";")
sb.AppendLine("var pin = new VEPushpin(")
sb.AppendLine("pinID, ")
sb.AppendLine("new VELatLong(" & _
UserIPInfoList.Instance(key).CoordinatesReversed & "), ")
sb.AppendLine("null, ")
sb.AppendLine("'" & UserIPInfoList.Instance(key).IPAddress & "', ")
sb.AppendLine("'" & UserIPInfoList.Instance(key).Location.Replace("'", _
"’") & "','pinEvent', 'Century 16'")
sb.AppendLine(");")
sb.AppendLine("")
sb.AppendLine("map.AddPushpin(pin);")
count = count + 1
Next
sb.AppendLine("}")
'add script to output.
Me.Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _
"MapScript", sb.ToString(), True)
C#{
//set number of users online.
this.numonline.Text = UserIPInfoList.Instance.Keys.Count;
//add pinpoints to map.
StringBuilder sb = new StringBuilder();
int count = 1;
sb.AppendLine("function ShowPins()");
sb.AppendLine("{");
foreach (string key in UserIPInfoList.Instance.Keys) {
sb.AppendLine("var pinID = " + count + ";");
sb.AppendLine("var pin = new VEPushpin(");
sb.AppendLine("pinID, ");
sb.AppendLine("new VELatLong(" +
UserIPInfoList.Instance(key).CoordinatesReversed + "), ");
sb.AppendLine("null, ");
sb.AppendLine("'" + UserIPInfoList.Instance(key).IPAddress + "', ");
sb.AppendLine("'" +
UserIPInfoList.Instance(key).Location.Replace("'",
"’") + "','pinEvent', 'Century 16'");
sb.AppendLine(");");
sb.AppendLine("");
sb.AppendLine("map.AddPushpin(pin);");
count = count + 1;
}
sb.AppendLine("}");
//add script to output.
this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
"MapScript", sb.ToString(), true);
}
Add this to the <!--call load map script when page loads-->
<body onload="GetMap();ShowPins();">
Step 5: Customizing your CSSThere are a few customizations you will need to make to your CSS; the first is to control the display of the modal window as well as the pins on the map. The second is to add a /*Modal Popup*/
.modalBackground {
background-color:Gray;
filter:alpha(opacity=50);
opacity:0.7;
}
.modalPopup {
background-color:#f9f9e5;
border-width:1px;
border-style:solid;
border-color:Gray;
padding:3px;
width:400px;
text-align:center;
}
/*Map*/
.pinEvent
{
width:10px;height:15px;
overflow:hidden;
cursor:pointer;
}
<style type="text/css">
.ero{z-index: 100002 !important;}
.ero-progressAnimation{z-index: 100002 !important;}
.VE_Message{z-index: 100002 !important;}
</style>
Points of interest
History
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||