Tuesday, November 15, 2011

Creating a Tag Cloud in ASP.NET


In the following article you'll find the method to create Tag Cloud in ASP.Net,



This article is

By Scott Mitchell





Introduction


tag cloud is a way to display a weighted list such that the weight of each item is reflected by the size of the item's text. Tag clouds provide a quick way for one to eyeball a list and ascertain what items are more prevalent. Oftentimes, each item in a tag cloud is rendered as a link that, when clicked, allows the user to drill into the selected category. For example, given any parent/child relationship in a database, we may be interested in seeing how many children each parent has, relative to one another. The following image of a tag cloud lists the categories in the Northwind database, with the size of each item is proportional to the number of products for each category, relative to the other categories. (Each category name, if clicked, takes the user to a page that lists the products for that particular category.)


A tag cloud.
Tag clouds are commonly used in websites that support tagging. Tagging is a technique of assigning a set of string values to a particular item - such as classifying a picture of your pet dog at a photo sharing website with string tags like "dog", "pet", "adorable", and so on. Tags provide an informal way for users to easily classify content and make it easy to visualize the most common tags and to drill down into a specific tag. However, tag clouds are not limited to websites that support tagging and can be used to offer an alternative view of a weighted list (such as parent/child data in a database).
In this article we'll examine how to build a tag cloud in ASP.NET. In this article we'll focus on accomplishing this through code in an ASP.NET page's code-behind class. In a future article, we'll move this code out of the ASP.NET page and into a stand alone, custom, compiled server control that supports data binding, use of declarative data source controls, and so on. Read on to learn more!

Deciding on the Tag Cloud's Font Sizing


When designing a tag cloud, it is important to first determine how the font sizes will vary by item. The font sizes can be specified using relative font sizes - xx-small, medium, x-large, and so on - or by absolute font sizes - 12pt, 32pt, etc. The benefit of using relative font sizes is that the fonts will scale based on a user's browser's text size settings. Another design decision to make is the spectrum of font sizes. That is, how many distinct font sizes are allowed? For the code we'll examine shortly, I've decided to use seven absolute font sizes: xx-small, x-small, small, medium, large, x-large, xx-large.Another vital design decision is how the font sizing is selected based on the distribution of the weights. For my code, I've decided to use a simple linear distribution. If the weighted list ranges from a minimum value of, say, 60, to a maximum value of 200, then I break down each of the seven font sizes into ranges plotted linearly along the minima and maxima. In this example, the delta is 140 (200 - 60). Divided by 7 results in each font size having a range of 20 units. So for any list items whose weight is between 60 and 80, I use font size xx-small; for those between 80 and 100, I use x-small; and so on.
While this linear scale is simple to implement and works well for evenly distributed data points, if you have skewed data points you'll end up with potentially extreme results. Consider the case where the weighted list being displayed has one data point with a weight of 60 (our minimum) and five data points between weights 180 and 200. The result is that the first data point will be displayed with the smallest font size (xx-small) and all remaining five will be displayed in the largest font size (xx-large). But this doesn't show the variation between the five in the 180-200 range. To capture this, an alternative model is to base the font sizes on the standard deviation of the distribution, which captures how far each data point deviates from the mean (the average). For more on this, check out Cloud Control for ASP.NET. It's an open source tag cloud control for ASP.NET created by Rama Krishna Vavilala that uses the standard deviation model.

Building the Cloud


Ideally, a tag cloud could be associated with some data, a few properties set, and, voila, the tag cloud appears! In fact, we'll examine how to accomplish exactly this in a future article by creating a custom, compiled ASP.NET 2.0 server control. For now, though, let's just implement the tag cloud directly from an ASP.NET page (although this could be moved to a User Control for greater reuse opportunities).First things first - we need the data that returns the list with each item weighted. In the demo downloadable at the end of this article, I have used a SqlDataSource control to query the Northwind database, returning the CategoryIDCategoryName, and number of products belonging to each category:


SELECT Categories.CategoryID, Categories.CategoryName, 
       COUNT(Products.ProductID) AS NumberOfProducts 

FROM Categories 
    INNER JOIN Products ON 
        Categories.CategoryID = Products.CategoryID 

GROUP BY Categories.CategoryID, Categories.CategoryName
ORDER BY Categories.CategoryName

This query uses the GROUP BY clause to return the count of products associated with each category. See Using the GROUP BY Clause for more information on this SQL clause.
The tag cloud is outputted in the Web page via a Literal Web control named CloudMarkup. In code we're going to loop through the database results, compute the font size scale, and then emit an HTML hyperlink as markup into the Text property of the Literal control. To start, we need to get the data from the SqlDataSource control. This is accomplished by calling its Select() method, which returns a DataView object:

'First, read data from SqlDataSource
Dim cloudData As DataView = CType(CategoriesProductsBreakdownDataSource.Select(DataSourceSelectArguments.Empty), DataView)

Next, a series of constants are defined in an attempt to generalize this code at least a little bit. For example, there are constants that define the names of the database columns that return the weight, the text field to display, along with the field to use (and a format string) when constructing the URL for each hyperlink. You'll also find the set of font sizes and the markup to inject between each link.

Const SpacerMarkup As String = " " 'The markup injected between each item in the cloud
Dim FontScale() As String = {"xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large"}

'All database column names are centralized here. To customize this, simply modify the column names here
Const WeightColumnName As String = "NumberOfProducts"
Const TextColumnName As String = "CategoryName"
Const NavigateUrlColumnName As String = "CategoryID"
Const NavigateUrlFormatString As String = "~/ViewProductsByCategory.aspx?CategoryID={0}"

Next, we need to determine the minimum and maximum weight values in the list. This information is then used to compute the linear scale by which we'll map an item's weight to a font size. ThescaleUnitLength holds the "length" of each notch on the scale.

Dim minWeight As Decimal = Decimal.MaxValue, maxWeight As Decimal = Decimal.MinValue

For Each row As DataRowView In cloudData
    Dim numProductsObj As Object = row(WeightColumnName)
    If Not Convert.IsDBNull(numProductsObj) Then
       Dim numProductsDec As Decimal = Convert.ToDecimal(numProductsObj)

       If numProductsDec < minWeight Then minWeight = numProductsDec
       If numProductsDec > maxWeight Then maxWeight = numProductsDec
    End If
Next

Dim scaleUnitLength As Decimal = (maxWeight - minWeight + 1) / Convert.ToDecimal(FontScale.Length)

After computing the scale, the data is enumerated one more time, this time with a hyperlink (<a>) element emitted for each record. To find the place on the scale, the current item's weight is subtracted from the minimum and divided by scaleUnitLength. This index is used to select the appropriate font size from FontScale. Also note that the specified values for NavigateUrlColumnName andNavigateUrlFormatString are used to configure the href portion of the hyperlink.

For Each row As DataRowView In cloudData
    Dim numProductsObj As Object = row("NumberOfProducts")
    If Not Convert.IsDBNull(numProductsObj) Then
       Dim numProductsDec As Decimal = Convert.ToDecimal(numProductsObj)

       Dim scaleValue As Integer = Math.Truncate((numProductsDec - minWeight) / scaleUnitLength)
       CloudMarkup.Text &= String.Format("<a href=""{0}"" style=""font-size:{1};"">{2}</a>{3}", _
                                    Page.ResolveUrl(String.Format(NavigateUrlFormatString, row(NavigateUrlColumnName).ToString())), _
                                    FontScale(scaleValue), row(TextColumnName).ToString(), SpacerMarkup)
    End If
Next

That's all there is to it! The resulting output is a chunk of HTML that, when rendered in the user's browser, lists each category as a hyperlink pointing to ViewProductsByCategory.aspx?CategoryID=categoryID. Each link's text size is based on its weight using a linear scale. The following screenshot below shows a tag cloud of the Northwind database's categories table along with the raw data used to populate the cloud.



A tag cloud along with the raw data.


Conclusion


Tag clouds can provide an alternative means for displaying a weighted list. As we saw in this article, they can be implemented with just a bit of code (although the standard deviation approach takes a bit more effort), and offer visual cues and a way to quickly drill into the details of any item. In a future article we'll look at how to move this logic to a custom, compiled ASP.NET 2.0 server control. Until then.....Happy Programming!

No comments:

Post a Comment