Friday, September 9, 2011

How To: SharePoint 2010 Weather Web Part

Over the years working with SharePoint I have had many requests for a weather web part to be placed on the home page of a company’s intranet or web site. The problem has always been that the RSS feeds you find online are never that reliable, and even if you do find one the data that it returns is never structured in an easy-to-use way.

I have recently found out that Google has its own weather feed. The data is structured perfectly for what I want to do, and the following will show how I went about creating a custom weather web part. I will be adding properties for the user to select if they want the Current Conditions or Tomorrow’s Forecast and the City they live in. (For now these will only be South African cities.)

1. Create the Project in Visual Studio
Open up visual studio and select to create a new Visual Web Part project under the SharePoint > 2010 templates, enter a name and click OK. On the next window enter your site URL and click Finish.
For purposes of keeping my projects tidy I have renamed my Web Part, the user control and the class files to more appropriate names.




2. Set up the user control display

I have added the following to my WeatherWebPartUserControl.ascx file that will render the data:

<style type="text/css">
    .wwp-container { width: 200px; text-align: center; font-family:Arial; font-size:12px; }
    .wwp-city { font-size:14px; font-weight:bold; }
    .wwp-error { color:Red; }
</style>
<div class="wwp-container">
    <asp:Label runat="server" ID="lblCity" CssClass="wwp-city"></asp:Label><br />
    <asp:Label runat="server" ID="lblDate" CssClass="wwp-date"></asp:Label><br />
    <asp:Image runat="server" ID="lblImage" CssClass="wwp-image"></asp:Image><br />
    <asp:Label runat="server" ID="lblTemp" CssClass="wwp-temperature"></asp:Label>&nbsp;-&nbsp;
    <asp:Label runat="server" ID="lblCondition" CssClass="wwp-condition"></asp:Label>
    <asp:Label runat="server" ID="lblError" CssClass="wwp-error"></asp:Label>
</div>

3. Update the Web Part class

In your web part class file you are going to add properties for the web part that will display when you edit the web part in your page. These settings make it easy for users to control how the web part works:



Here's the code:

// enums for the values of the drop downs
public enum CityEnum { None, Johannesburg, Cape_Town, Durban, Pretoria, Bloemfontein };

public enum ForeCastDayEnum { Today, Tomorrow };
// public properties
public static CityEnum City;
public static ForeCastDayEnum ForeCastDay;

// these settings tell the web part how to display the controls
[Category("Settings"), Personalizable(PersonalizationScope.Shared), WebBrowsable(true), WebDisplayName("City"),
DefaultValue("None"), WebDescription("Select a city")]
public CityEnum _City
{
 get { return City; }
 set { City = value; }
}
// these settings tell the web part how to display the controls
[Category("Settings"),
Personalizable(PersonalizationScope.Shared), WebBrowsable(true), WebDisplayName("Forecast Day"),
DefaultValue("Today"), WebDescription("Select a city")]
public ForeCastDayEnum _ForeCastDay
{
 get { return ForeCastDay; }
 set { ForeCastDay = value; }
}

4. Update the User Control class

The class of the user control is where most of the processes happen. Here the methods are contained for accessing the RSS data and transforming it into displayable data. Below are the methods that I used. Comments are in line.

public const string _xmlUrl = "http://www.google.com/ig/api?weather={0},ZA&hl=en";
public const string _webUrl = "http://www.google.com/";
protected void Page_Load(object sender, EventArgs e)
{
 lblError.Text = string.Empty;
 try
 {
  WeatherWebPart.CityEnum selectedCity = WeatherWebPart.City;
  // check if a city has been selected
  if (selectedCity == WeatherWebPart.CityEnum.None)
  {
   throw new Exception("No city has been selected from the web part settings.");
  }
  else
  {
   GetWeatherData(selectedCity);
  }
 }
 catch (Exception ex)
 {
  lblError.Text = ex.Message;
 }
}

private void GetWeatherData(WeatherWebPart.CityEnum city)
{
 // format the url
 string url = String.Format(_xmlUrl, city.ToString().Replace("_", " "));
 // create the xmlreader
 XmlTextReader reader = new XmlTextReader(url);
 // load the reader into an xmldocument
 XmlDocument doc = new XmlDocument();
 doc.Load(reader);
 // get the root node
 XmlNode rootNode = doc.SelectSingleNode("/xml_api_reply/weather");
 // get the forecast data
 string cityName = rootNode.SelectSingleNode("forecast_information/city").Attributes["data"].Value;
 DateTime forecastDate = Convert.ToDateTime(rootNode.SelectSingleNode("forecast_information/forecast_date").Attributes["data"].Value);
 // check if the web part displays today or tomorrows weather
 if (WeatherWebPart.ForeCastDay == WeatherWebPart.ForeCastDayEnum.Today)
 {
  ShowToday(rootNode, cityName, forecastDate);
 }
 else
 {
  ShowTomorrow(rootNode, cityName, forecastDate);
 }
}

private void ShowTomorrow(XmlNode rootNode, string cityName, DateTime forecastDate)
{
 // get tomorrows conditions
 string tomorrowCondition = rootNode.SelectSingleNode("forecast_conditions[2]/condition").Attributes["data"].Value;
 string tomorrowHigh = rootNode.SelectSingleNode("forecast_conditions[2]/high").Attributes["data"].Value;
 string tomorrowLow = rootNode.SelectSingleNode("forecast_conditions[2]/low").Attributes["data"].Value;
 string tomorrowIcon = _webUrl + rootNode.SelectSingleNode("forecast_conditions[2]/icon").Attributes["data"].Value;
 string temperature = String.Format("{0}&deg;C - {1}&deg;C", tomorrowLow, tomorrowHigh);
 UpdateWebPartDisplay(cityName, tomorrowCondition, forecastDate.AddDays(1), temperature, tomorrowIcon);
}

private void ShowToday(XmlNode rootNode, string cityName, DateTime forecastDate)
{
 // get the current conditions
 string currentCondition = rootNode.SelectSingleNode("current_conditions/condition").Attributes["data"].Value;
 string currentTemperature = rootNode.SelectSingleNode("current_conditions/temp_c").Attributes["data"].Value + "&deg;C";
 string currentIcon = _webUrl + rootNode.SelectSingleNode("current_conditions/icon").Attributes["data"].Value;
 UpdateWebPartDisplay(cityName, currentCondition, forecastDate, currentTemperature, currentIcon);
}

private void UpdateWebPartDisplay(string city, string condition, DateTime date, string temperature, string iconUrl)
{
 lblCity.Text = city;
 lblCondition.Text = condition;
 lblDate.Text = date.ToLongDateString();
 lblTemp.Text = temperature;
 lblImage.ImageUrl = iconUrl;
}

5. Deploy the web part and install it on the page.

You can deploy the web part by simply right-clicking your project file and clicking Deploy. The solution will deploy to the URL that you specified in the setup stages of the project.

Here is the final look of the web part:


8 comments:

  1. This project is giving forbidden:403 error. Plz advice.

    Chaitanya

    ReplyDelete
    Replies
    1. Hi Krishna,

      Google has recently taken their weather API offline and is not available for public use anymore. I would suggest using the Yahoo weather service as this makes a good replacement.

      Delete
    2. Thats fine but plz advice how to incorporate yahoo weather in the above code. I mean where the modifications are exactly required.

      Delete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Hi Ghecko, This one is very nice article. I have doubt, if i use yahoo api those api methods are same for both yahoo and google?? if possible can u provide source code with yahoo api?

    Thanks.

    ReplyDelete
    Replies
    1. Hi Uday,

      Unfortunately I don't have source for the Yahoo one. Yahoo actually is a bit more difficult because they don't store the weather values like Google - they generate their own html and store it in a CDATA tag.

      The last source I used was from a paid provider which was at a client - but only minor modifications had to be made to the xml.

      Delete
  4. This comment has been removed by a blog administrator.

    ReplyDelete