Plotting Your Data Using Virtual Earth

Plotting Your Data Using Virtual Earth

Rate This
  • Comments 49

Jon  Hello SharePoint Designer users,

My name is Jon Campbell – I’m a developer for the SharePoint Designer team. My main job is to work on all things data oriented in SharePoint Designer. Today I thought I would share a tip on how to take your data views to the next level by combining some custom XSL transforms on the server with JavaScript on the client. Specifically, this post will go through the steps of how to make a data view that will show a set of points stored in a data source, such as a SharePoint list, on a map using Virtual Earth.

We have an XML file that contains the data that we want to plot as pushpins on the Virtual Earth map. The data can come from any data source, but for the sake of simplicity an XML file will do the job. The most important aspect of the data is that it must have both latitude and longitude so that it can be plotted. Without a latitude and longitude we would need to use an address and geocode it to the equivalent coordinates, which is outside the scope of this post. Create an XML file called locations.xml and set the contents as follows:

<?xml version="1.0" encoding="utf-8" ?>

<Locations>

      <Location lat="47.760101" long="-122.205141" name="place 1" description="A nifty place." />

      <Location lat="47.255436" long="-122.51739" name="place 2" description="A place for fun!" />

      <Location lat="47.831876" long="-122.277658" name="place 3" description="More fun!" />

</Locations>

Next, we need to be able to show the Virtual Earth map on the page. The basic requirements for doing so are including the Virtual Earth map control, creating a div tag to show the content, and then adding a basic script to use the map control to place the map into the div tag. Create a new ASPX page and switch to code view. Put the following code inside the form tag:

<script src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=5">    </script>

    <script type="text/javascript">

      var map = null;


// Loads the Virtual Earth map control

      function GetMap()

      {

            map = new VEMap('myMap');

            map.LoadMap(new VELatLong(47.6, -122.2), 8,'r' ,false);

            AddPin(47.7,-122.2,null,'place 1','A nifty place to be.');

      }

     

// Places a pushpin on the map using the parameters given, iconurl is ignored

      function AddPin(lat, lon, iconurl, title, desc)

      {

            var shape =

new VEShape(VEShapeType.Pushpin,

new VELatLong(lat,lon));

            shape.SetTitle(title);

            shape.SetDescription(desc);

            map.AddShape(shape);

      }

 

// Programmatically adds func as a handler for the onload event

// This method has been used by many developers, but the code is

// via the ViaVirtualEarth Wiki

// http://www.viavirtualearth.com/Wiki/Load+VE+control+without+body+onload.ashx

      function addLoadEvent(func)

      {

            var oldonload = window.onload;

            if (typeof window.onload != 'function')

            { window.onload = func; }

            else

            { window.onload = function()

                  { oldonload(); func(); }

            }

      }

      addLoadEvent(GetMap);

    </script>

    <div id='myMap' style="position:relative; width:400px; height:400px;"></div>

The last function, addLoadEvent(func), is used to run the map control’s code when the page is loaded without having to set the body tag’s onload event handler. This can be useful in situations where a master page is being used. If you preview the page, you should end up with a map like the one to the right.

Next you should insert a DataFormWebPart view of the locations.xml file that you created earlier. There are many methods for doing this, but the easiest way for what needs to be done is switch to design view and then drag it from the folders list and drop it onto the page. You can then switch back to code view. You should be able to find the XSL property of the DataFormWebPart that was created. Before the dvt_1 template, add the following XSLT:

<xsl:template name="AddMapPins">

    <xsl:param name="Rows"/>

    <xsl:text disable-output-escaping="yes"><![CDATA[

            <script type="text/javascript">

            function AddPins()

            {

            ]]></xsl:text>

    <xsl:for-each select="$Rows">

      <xsl:if test="not(normalize-space(@lat) = '' and normalize-space(@long) = '')">

        AddPin(<xsl:value-of select="@lat" />,

        <xsl:value-of select="@long" />,

        null,

        '<xsl:value-of select="@name" />',

        '<xsl:value-of select="@description"/>');

      </xsl:if>

    </xsl:for-each>

    <xsl:text disable-output-escaping="yes"><![CDATA[

            }

            </script>

            ]]></xsl:text>

</xsl:template>

That XSLT code will iterate over each of the rows in the dataset and create individual calls to AddPin() inside the AddPins() function. The AddPin() function uses the latitude and longitude from the row in the dataset as well as the name and the description attributes.

There are a couple things which need to be done in order to hook up the DataFormWebPart to the JavaScript code. The first is that the AddMapPins template won’t get called yet. To ensure that it gets called, add the following lines inside the dvt_1 template immediately after the Rows variable.

<xsl:call-template name="AddMapPins">

  <xsl:with-param name="Rows" select="$Rows"/>

</xsl:call-template>

Next, change the AddPin(…) call in GetMap() to be a call to AddPins(). The resulting GetMap() function should look like this:

function GetMap()

{

      map = new VEMap('myMap');

      map.LoadMap(new VELatLong(47.6, -122.2), 8,'r' ,false);

      AddPins();

}

If everything is correct then you should be able to preview the page and end up with the results shown in the screenshot to the right. This method demonstrates how client side script can be generated based on server side data using XSLT. Though JavaScript was used in this example, it is not unreasonable to imagine using this to drive other client side technologies like SilverLight. This won’t replace custom web parts, but it definitely opens up some interesting scenarios – especially for those in hosting scenarios or with limited posting privileges.

Enjoy,
Jon

Attachment: SampleMappingSource.zip
  • Dear Jon,  I have a similar case like Dacle where I want to display multiple address on a map in VE.  I've already created a sharepoint list with address (there's lats and longs including it).  However, I don't know how to get those data from the list and post them as pushpins in the VE inside SharePoint...

  • With regard to the comments about driving directions - its super easy to do! You don't even need to have the virtual earth map or anything, you can just format your address and you are good to go. That url has info on building your own url.

    http://help.live.com/Help.aspx?market=en-US&project=WL_Local&querytype=topic&query=WL_LOCAL_PROC_BuildURL.htm

    For example, if you were going to go to Chicago but wanted to let the user put in their own starting point then you could use a url like this:

    http://maps.live.com/default.aspx?v=2&rtp=~adr.Leavenworth,%20Washington">http://maps.live.com/default.aspx?v=2&rtp=~adr.Leavenworth,%20Washington

    In the sharepoint contacts list case, you would probably want something like this:

    <a href="http://maps.live.com/default.aspx?v=2&rtp=~adr.{@WorkAddress},{@WorkCity},{@WorkState},{@WorkZip}">Get directions!</a>

  • Hi,

    Its a great post.

    I am strucked with some problem using Javascript in xslt.

    I have javascript on my xsl file.

    In the javascript I have a method, which return some databsed on browser locale.

    Based on the value returned by the javascript function I want to set the value for xslt variable or parameter.

    I want to use this xslt parameter value and include some other xsl on condition basis.

    How to set the xslt variable value from the javascript. My Javascript is there in the xslt itself.

    My requirement is, I want to include culture specific xsl in my main xsl based on the value from Javascript.

    Any help will be highly appreciated.

  • mswin: The situation you are describing is somewhat backwards to what we are doing in this post. The method described above uses xsl on the server to generate javascript that is inserted into the page markup, which in turn is run by the client. What you seem to be describing is the reverse, which is a rather different scenario.

    While your scenario is somewhat vague to me, in general you can pass values to the DFWP using parameters. Those parameters are in turn available to the xsl as <xsl:param> values. So, if you have javascript that you want to pass a resource to a DFWP via the query string, you can create a query string parameter on the DFWP and then have your javascript open the appropriate page passing in the data that you got via your call.

    If that doesn't make sense then you can contact me offline at joncamp at microsoft dot com and then we can discuss if this method will suit your needs.

  • Jon,

    Is the use of this code to display a virtual earth map in a Sharepoint Intranet Portal depend on us having a MWS/VE  agrrement/license?

  • judysemo: You should check the licensing terms for Virtual Earth since I am not a lawyer, but the above code is just like any other mashup or VE application that any site would use. I think http://www.microsoft.com/virtualearth/product/terms.html has the info that you are looking for. From the looks of it, as long as you arent hitting 100k transactions in a 24 hour period you should be fine, but check the terms to be sure.

  • Jon,

    I'm also interested in the answer to Judysemo's question. The third paragraph of the terms states "You are not permitted to use the API or the Service on any intranet or non-public website, unless You have a MWS/VE Agreement that includes such rights."

    I've expirimented with the Virtual Earth API on an intranet and it does work. On the other hand, the Google Maps API actually prevents use on an intranet because of similar terms.

    Your answer and the fact that it actually works on an intranet suggest there is some gray area in the terms. Do you know if that is true?

    Tom

  • Tom: I wish I could give you an easy answer, but I am not a lawyer. The best I can do is suggest that you or your corporate legal affairs team take a look at the licensing terms. Perhaps checking on the Virtual earth forum (http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=537&SiteID=1) might shed more light.

  • Is there any way to display a more minimal zoom menu ?  I have this in a sort-of small part of my page [about 400x300px], and the zoom/menu kind of takes over.

    I'm also looking to use some non-standard icon, I see that the AddPin() function is called with an 'iconurl' parameter, but it doesn't seem to be used in the actual function.  Is it still, just not visibly ?  Also, how should I align my icons so that the pointer-part of it is still in the same place ?

    I cast my vote for built-in geocoding support too.  I'm fine with using batchgeocode or something, but my nontechnical clients get confused easily. :)

    Also, using non-integer zoom levels is rather amusing, nice error handling. :P

  • I've solved my own problems, if anybody else wants them:

    You can hide the zoom/navigation menu by applying the following stylesheet:

    "#myMap_dashboard {visibility:hidden!important;}"

    Custom icons can be used by implementing this:

    Change the 'null' in the original call to AddPin() to a link to an image that you want to use instead of the default.

    Add this line to the AddPin() function: "shape.setCustomIcon(iconurl)", before the map.AddShape() part, but after the shape object is created.

    Have fun!

  • Body: I threw together a quick demo http://www.wssdemo.com/Pages/map.aspx of the code demonstrated in

  • Dash, I am trying to change the icon for the pushpin. but after settiing it to a custom image, pushpins are not displayed at all.

    The code I used is:

         function AddPin(lat, lon, iconurl, title, desc)

         {

               var shape = new VEShape(VEShapeType.Pushpin, new VELatLong(lat,lon));

               shape.setCustomIcon("<div><img src='dot.gif'/></div>");

               shape.SetTitle(title);

               shape.SetDescription(desc);

               map.AddShape(shape);

         }

    and on a note dot.gif is sitting at the same place where my aspx page is.

    Any help is highly appreciated. Thanks

  • I got this working perfectly. just changed the shape.setCustomIcon to shape.SetCustomIcon. But it was really pain in butt to figure it out.

    Now I want a link in the popup which will open a different list on the same or different page. I was wondering if anybody can help...Thanks.

  • I really appreciate what you've shown me here.  I was able to disect your code and in turn change the data source from the XML into the one of the lists on my site.  Unfortunately all I had was addresses in my list so I geocoded the info to get my LatLong info and now I have a list that is plotted against a VE map.  Thanks again.

  • Nitu: To combine data from multiple lists, there are two ways that i have tried successfully.

    1) instead of using an SPDatasource, use an aggregate datasource which contains the two lists you want data from. Then just manipulate the XSL to point to the right places.

    2) use the "hidden iframe" technique to load the second page into a hidden iframe, then use javascript to pull data out of that page into the div for the popup.

Page 2 of 4 (49 items) 1234
Leave a Comment
  • Please add 2 and 2 and type the answer here:
  • Post