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
  • Hi, thanks for this. I have modified the code to use a list of input and I have changed to version 6 of the API. All works great.

    I want to now improve my solution:

    Once I have plotted the points I want to center and scale the map to show all the plotted points.

    I was hoping to do this by using the VEMap.SetMapView method. This method needs an array of the plot points. That is straight forward (I think)  I just created an array and I push the VELatLong objects into it just after I have plotted them.

    However, I am very new to xslt processing and my big problem is where/when to run VeMap.SetMapView method. I thought I had found the place that I thought it was logical to put it but that did not work.

    Any help with this would be appreciated.

    Thanks

    Simon

  • Simon - based on what you are describing, you will want to modify the AddMapPins template. The code above is pretty simple in that it creates an AddPin() call for each row of data. A better way would be to have the AddMapPins template generate the array of points. Something similar to the following should be close to what you want.

    <xsl:template name="AddMapPins">

       <xsl:param name="Rows"/>

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

               <script type="text/javascript">

               var mypins = {

               ]]></xsl:text>

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

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

           new VEShape(VEShapeType.Pushpin, new VELatLong(<xsl:value-of select="@lat" />,<xsl:value-of select="@lon" />)),

         </xsl:if>

       </xsl:for-each>

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

               }

               </script>

               ]]></xsl:text>

    </xsl:template>

    Then you would want to modify the GetMap() function to call your SetMapView with those pins, and also modify the addpins function to add each of your pins to the map. http://dev.live.com/virtualearth/sdk/Ref/HTML/M_Namespace_VEMap_SetMapView.htm has a good example of the basic concept.

  • I could realy do with some help here guys.

    Im not technically minded with ASP.NET (PHP MYSQL background)

    Ive done a course in Sharepoint, installed it, set up Sites etc.

    Do I edit a Sharepoint master page and drop the code in (the code above)?

  • HI

    i have an issue

    My client has asked me to put the org chart on the page (which is big and which is a visio file)

    and the org chart contains the list of the companies

    client has also asked, when the file is displayed on the page one must be able to zoom on the page so  the name of the company can be seen

    i am new to coding and designing

    Please help ...........my mail id is tonidecoster9@yahoo.com

    Regards,

    toni decoster

  • very nice job

    but my task is slightly different

    i have to show the offices of a company that is in share point list site on the virtual earth map

    like if i click the US office of the company then it should show the address of that US office in the map

    plz help me

    it is very urgent

    regards

    Ram

    plzzzzzzzzzzz

    hury

  • Hi everyone, Jon here again with a quick how-to on creating a simple bar graph of your data by using

  • Hi,

    I want to use this functionality on my SharePoint homepage, using a custom list in my SharePoint site as the data source. I was wondering how would I implement list VE map on my homepage based off a custom list that is not displayed on the page. Also, how could I implement this mapping as is using a custm list instead of creating a new XML file. I am some what new to XML and this type of customization so any help would be greatly apprecaited.

  • Aaron - you don't have to show the list on the page - I just did that for clarity's sake. If you don't want the table to be shown, just delete the <table> tag that is after the call to AddMapPins. For completeness you should delete the dvt_1.body and dvt_1.rowview templates, but you don't have to.

    In terms of using a list instead of the xml file - the process is basically the same. the biggest difference is that inside the AddPin template you will need to change the fields that are referenced. for example, if you call your latitude field "Latitude", then you will want to change the "@lat" to be "@Latitude".

  • Hi everyone, Jon here again with a great tip on visualizing your data. In my Plotting Your Data Using

  • Is there a way to load the map into an iFrame, but still pull from the Data source in the main page?

  • Also, Has anyone tried to load mutiple data soruces in the map using layers? I have tried but with no success. Below is my DIV tag to load the map.

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

       <script type="text/javascript">

         var map = null;

               var layer1;

               var layer2;

         function GetMap()

         {

               map = new VEMap('myMap');

               map.SetDashboardSize(VEDashboardSize.Small);

               map.LoadMap(new VELatLong(44.4024, -94.1309), 6,'r' ,false);

               AddPins();

            CreateLayer1();

            CreateLayer2();

         }

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

         {

               var shape =

    new VEShape(VEShapeType.Pushpin,

    new VELatLong(lat,lon));

               shape.SetCustomIcon("icon.gif");

               shape.SetTitle(title);

               shape.SetDescription(desc);

              layer.AddShape(shape);

         }

         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:600px; height:400px;"></div>

    This is how I am trying to call the pins.

    <xsl:template name="AddMapPins">

       <xsl:param name="Rows"/>

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

               <script type="text/javascript">

               function CreateLayer1()

               {

    alert();

                             layer1 = new VEShapeLayer();

                             map.AddShapeLayer(layer1);

               ]]></xsl:text>

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

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

           AddPin(layer1, <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>

    -------------------

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

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

    </xsl:call-template>

    Any help on this would be greatly appreciated. I have reached out to John for this, but we could figure it out.

  • I figured out my problem. I was not calling the PINs for each  layer. Below is the code that worked for me.

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

       <script type="text/javascript">

         var map = null;

               var layer1;

               var layer2;

         function GetMap()

         {

               map = new VEMap('myMap');

               map.SetDashboardSize(VEDashboardSize.Small);

               map.LoadMap(new VELatLong(44.4024, -94.1309), 6,'r' ,false);

            CreateLayer1();

            CreateLayer2();

               AddPins();

         }

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

         {

               var shape =

    new VEShape(VEShapeType.Pushpin,

    new VELatLong(lat,lon));

               shape.SetTitle(title);

               shape.SetDescription(desc);

              layer1.AddShape(shape);

         }

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

         {

               var shape =

    new VEShape(VEShapeType.Pushpin,

    new VELatLong(lat,lon));

               shape.SetTitle(title);

               shape.SetDescription(desc);

              layer2.AddShape(shape);

         }

         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:600px; height:400px;"></div>

  • Woww,great job...

    Thanks Jon for your knowledge sharing :) :)

  • Hello Jon

    Nice piece of coding.

    I tested on my local server, working nicely. But I am thining in some different manner, can I not first generate xml from an existing sharepoint list and load this xml in your suggested coding.

    OR

    Can I direct integrate sharepoint list rather than hardcoded location.xml?

    Please advise

  • I have it working from the xml file with no issues, i've also been able to modify various aspects of it, but one thing I cannot get to work is getting it to read from a sharepoint list.  I basically tried to replace the paths that would point to the xml file and make them point to the location of the list, but that only resulted in an error at the bottom of the map.  I changed the datasources names to represent the fields but that didn't do anything either.  I'm not a coding or sharepoint guru to being with, can someone point me to a sample code that shows how to reference a sharepoint list rather than an xml file? Thanks in advance.

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