Hi everyone,
My name is Greg Chan and I’m a Program Manager in the SharePoint Designer team. I’ve also been involved in the development and testing of the Application Templates for Windows SharePoint Services 3.0. Just a couple weeks ago, I attended Tech Ed in Orlando and presented a breakout session called "Designing and Building Sophisticated Composite Applications with Microsoft Office SharePoint Designer 2007".
Today, I’ll show you how to integrate the Virtual Earth Map control with your SharePoint list. This is actually part of the demo I gave in Tech Ed, and it has generated a lot of interest. Many of you have requested more information on this, so as promised, I’m dedicating this post to it. Some of you may be wondering… "Isn’t this the same as the Plotting Your Data Using Virtual Earth blog that Jon Campbell posted awhile back?" While there are overlaps, the key difference is that this post will talk about plotting a single address as opposed to multiple addresses. A big benefit for doing single address is that Virtual Earth is good at plotting a single address without the need for longitudes and latitudes. We simply need to pass in the address value and take advantage of functions provided by Virtual Earth to geocode it. (I don’t know about you, but when I search for driving directions, I’m not typing in longs and lats...)
Let's get started!
Here’s the scenario -- you own a bike shop and use a SharePoint site to manage the manufacturing process for building bicycles. In your site, you have a Suppliers list that stores information about each of your supplier like phone number, address, e-mail, etc. The list is based off the Contacts list. You want to insert a map control inside the display form for each supplier to show the location of the supplier.
Step 1 - Modifying the Display Form
Open in SharePoint Designer the display form (DispForm.aspx) of the SharePoint list containing the address information. Then, we want to use the data view to generate the map. (Yet another reason why we call data view the swiss army knife of web parts...) Below are the detailed steps to do this:
Note - All standard SharePoint forms contain an ID parameter in the query section of the URL:
http://<site_name>/Lists/Suppliers/DispForm.aspx?ID=36
Each item in a SharePoint list is mapped to a unique ID value. This ID value is what we're using to filter the data view.
As a result, we’ve just inserted a data view of the suppliers list that is filtered down to show ONLY the current supplier and not all other suppliers. Right now, the data view is still rendered as a table by the default XSLT. Next step, we’re going to write custom XSLT and JavaScript to format the data view as a map!
Step 2 - Use custom XSLT + JavaScript to create Virtual Earth Map control
In order to create the Virtual Earth Map control, we’ll need to utilize some JavaScript functions provided by the Virtual Earth SDK. Below are JavaScript snippets that we need.
Load the map
var map; // Loads the Virtual Earth map control function loadMyMap() { map = new VEMap(&'myMap'); map.LoadMap(); map.FindLocation '<xsl:value-of select="@WorkAddress" />, <xsl:value-of select="@WorkCity" />, <xsl:value-of select="@WorkState" />, <xsl:value-of select="@WorkZip" />', displayPushPinCallBack); }
var map;
// Loads the Virtual Earth map control
function loadMyMap() {
map = new VEMap(&'myMap');
map.LoadMap();
map.FindLocation '<xsl:value-of select="@WorkAddress" />, <xsl:value-of select="@WorkCity" />, <xsl:value-of select="@WorkState" />, <xsl:value-of select="@WorkZip" />', displayPushPinCallBack);
}
FindLocation() will take the address values passed in and center the map to that location. This is a great function! For the function parameters, we’re passing in the XSL values of the Address, City, State and Zip Code field of the supplier.
Add the pushpin
function displayPushPinCallBack() { var pin = new VEPushpin( 1, map.GetCenter(), null, "<xsl:value-of select="@Title" />", '' ); map.AddPushpin(pin); }
function displayPushPinCallBack() {
var pin = new VEPushpin(
1,
map.GetCenter(),
null,
"<xsl:value-of select="@Title" />",
''
);
map.AddPushpin(pin);
GetCenter() will return the longitude and latitude values of the center of the map, which is currently the location of the address you passed in from FindLocation(). We’re also passing in the XSL value of the Title field of the supplier.
The entire XSL stylesheet
Here’s the entire XSL that we will use to format the data view into a Virtual Earth Map control.
<?xml version="1.0" encoding="utf-8" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" indent="no"/> <xsl:template match="/"> <xsl:call-template name="dvt_1"/> </xsl:template> <xsl:template name="dvt_1"> <xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row" /> <xsl:call-template name="dvt_1.body"> <xsl:with-param name="Rows" select="$Rows" /> </xsl:call-template> </xsl:template> <xsl:template name="dvt_1.body"> <xsl:param name="Rows" /> <!-- Link to the Virtual Earth Map control JS file --> <script src="http://dev.virtualearth.net/mapcontrol/v4/mapcontrol.js"> </script> <xsl:for-each select="$Rows"> <xsl:call-template name="dvt_1.rowview" /> </xsl:for-each> </xsl:template> <xsl:template name="dvt_1.rowview"> <div id='myMap' style="width:650px; height:400px;"></div> <script type="text/javascript"> _spBodyOnLoadFunctionNames.push(" loadMyMap "); var map; // Loads the Virtual Earth map control function loadMyMap() { map = new VEMap('myMap'); map.LoadMap(); map.FindLocation('<xsl:value-of select="@WorkAddress" />, <xsl:value-of select="@WorkCity" />, <xsl:value-of select="@WorkState" />, <xsl:value-of select="@WorkZip" />', displayPushPinCallBack); } function displayPushPinCallBack() { var pin = new VEPushpin( 1, map.GetCenter(), null, "<xsl:value-of select="@Title" />", '' ); map.AddPushpin(pin); } </script> </xsl:template> </xsl:stylesheet>
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="no"/>
<xsl:template match="/">
<xsl:call-template name="dvt_1"/>
</xsl:template>
<xsl:template name="dvt_1">
<xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row" />
<xsl:call-template name="dvt_1.body">
<xsl:with-param name="Rows" select="$Rows" />
</xsl:call-template>
<xsl:template name="dvt_1.body">
<xsl:param name="Rows" />
<!-- Link to the Virtual Earth Map control JS file -->
<script src="http://dev.virtualearth.net/mapcontrol/v4/mapcontrol.js">
</script>
<xsl:for-each select="$Rows">
<xsl:call-template name="dvt_1.rowview" />
</xsl:for-each>
<xsl:template name="dvt_1.rowview">
<div id='myMap' style="width:650px; height:400px;"></div>
<script type="text/javascript">
_spBodyOnLoadFunctionNames.push(" loadMyMap ");
map = new VEMap('myMap');
map.FindLocation('<xsl:value-of select="@WorkAddress" />,
<xsl:value-of select="@WorkCity" />,
<xsl:value-of select="@WorkState" />,
<xsl:value-of select="@WorkZip" />', displayPushPinCallBack);
</xsl:stylesheet>
Next thing you'll want to do is take that entire XSL stylesheet and save it out as a separate XSL file. This will make things a lot cleaner and easier to manage. We'll save this out into a file called LiveMaps.xsl, and store it in the folder for the Suppliers list, along with the DispForm.aspx.
Step 3 - Format the data view using LiveMaps.xsl
Now we want to use LiveMaps.xsl to render the data view.
All Done!
Back on the browser, when you go to the display form of any supplier, there’ll be a great map view that will show you the location of the supplier. You’ll be able to interact with the map, just like you would on http://local.live.com.
Thanks,
Greg
Michael - What you are describing is basically what is presented in the other mapping post on this blog - http://blogs.msdn.com/sharepointdesigner/archive/2007/05/23/plotting-your-data-using-virtual-earth.aspx. You would need to have your contacts list sync'd up with a sharepoint contacts list, and then plot that sharepoint list. Since you will need geocoding, check my response to anthony's question on how to get that done for a list.
Hey Guys,
I guess some of you guys have problem loading the map even following everythiong in the blog. It seems that the _spBodyOnLoadFunctionNames.push(" loadMyMap "); doesn't work at times. So I replaced that code with "function addLoadEvent(func)
{
var oldonload = window.onload;
if (typeof window.onload != 'function')
{ window.onload = func; }
else
{ window.onload = function()
{ oldonload(); func(); }
addLoadEvent(loadMyMap);
"
And this worked for me. Hope this helps.
Thanks
Nakul
Nakul - the most common reason why _spBodyOnLoadFunctionNames won't work is if you are on a page that doesn't use the default.master that ships with wss/moss out of the box. As you noted, the above code serves a similar function and will work on pages with or without the _spBodyOnLoadFunctionNames stuff.
Jon,
Thanks for pointing out the reason why _spBodyOnLoadFunctionNames doest work. I observed one more thing if I use this code and browse it using Safari, I don't see the map at all. I am not able to figure out what seems to be the reason.
Great job
but my job is slightly different that i have to show the offices of a company that r in my share point list.
like if i have to show of the US offices of the company,then i have to show the address in map of that particular office.
plz help me
Regards
Ram
Ram - This post should get you most of the way to what you want. If I understand you correctly, you are wanting to have the address shown with the pushpin in the popup that comes up? If so, then you want to modify the VEPushPin javascript call. The last two arguments are the title and description, respectively, which are displayed in the popup that is shown when you hover over the pushpin for the location. The code above sets the title to be the Title field (which is really the last name) and the description to be blank. You just need to change those last two arguments to reflect your desired behavior
Hi,
thats really cool and exactly what I need for a showcase. I got this to work straight a way. But know I'm trying ot re-build this for the second time and it doesn't work.
Does someone have any idea what might be wrong? I think I haven't changed anything.
Jan
Jan - I would suggest taking this code and translating it to the lates version (6.1 as of today) of the sdk (http://dev.live.com/virtualearth/sdk/). That might work better for you, and it should be a pretty straight forward task.
Almost there- it seems great so far!
Im getting an error when opening the display page. A map loads but it is only a map of the US, not a specific address. Here is what the error says:
The server is temporarily unavailable. Try again late.
Is this really a temporary error?
Solved my problem.
I had changed the xpaths thinking that it was causing a previous error. For example I made @WorkAddress just @Address and etc..
Hello Jon
rather than using the xml as a data source for the grid view,i have to use SQL as a data source
Is this possible?
Please help me
Ram Sharma
Ram - You can use any of the datasources that are supported by the DataFormWebPart, which means most ASP.Net DatasSourceControls. If you are using a SQL datasource then you will want create a DataView of your SQL data source, then adapt the above xsl to meet your needs. The main thing you will need to change are the <xsl:value-of /> calls to make sure that they refer to the appropriate fields in your sql datasource. There are a couple ways to do this, but an easy way is to insert a table view of your data, then look at the xsl in order to find the columns you want.
Hi Jon
Thanks for your reply, i have created data view of my SQL data source by following these steps:
1. Created a data table in SQL having columns name lat,long,name and description
2 Create a aspx page SP designer and write down the functions like GetMap,AddPin etc
3 open data source library from the task pane and expand Data Source Connections and connect to my data base that SQL data base.
4 and then right click the data source that appear in data source library,click on show data and it shows me the data'
But as you kindly said in your reply"then adapt the above xsl to meet your needs" and "The main thing you will need to change are the <xsl:value-of /> calls to make sure that they refer to the appropriate fields in your sql datasource."i am getting these point
Please guide me
First off, Great Post!! I actually took this code and hooked it into a BDC connection.
Now for my issue. I am getting an popup each time this page is loaded saying that “This page contains both secure and nonsecure items". I thought I could fix this by changing the "Mixed Mode" setting in IE but it did not fix it. Any thoughts on what I can do to prevent this popup?
John - This blog post should help fix your problem - it sounds like you are using https/ssl or whatnot. You basically need to use an ssl path to get to the map javascript. try changing http://dev.virtualearth.net/mapcontrol/v4/mapcontrol.js to be https://dev.virtualearth.net/mapcontrol/v4/mapcontrol.js. However, you should probably update the code to use the v6 api since VE seems to be retiring some of their older versions.
http://weblogs.asp.net/davidbarkol/archive/2007/11/23/ssl-support-for-virtual-earth-is-here.aspx