I wanted to add a TagCloud to GeekZilla and another bespoke development
project. The first thing I did was look around the web for some
examples (or free components). To my supprise none jumped out at me. So,
I decided to write my own.
At first I found myself wondering how I would render different size words randomly within a
. How would I be able to position them in relation to each other? float:right?? After looking at a few tag coulds de.licio.us , dotnetkicks etc I discovered that tag clouds are infact displayed in alphabetical order and the word size doesn't change much.
I created a method called GetCloud() which would contain the logic for the tag weighting. The method accepts a DataTable and a String based Cloud Template. The Cloud Template is tokenised, the replaced version is returned for each tag in the DataTable.
DataTable containing the columns 'tag' and 'count'/// My Needs
The TagCloud would need to be able to be generated from a DataTable containing the columns 'tag' and 'count'. In my case the data would come from a Sql Server 2005 database. At the database level I'd be doing the aggregation and ordering. The TagCould code would only need to worry about weighting the tags.The Data
First of all I created a view of all tags with their grouping:SELECT TOP (60) COUNT(1) AS Count, TagFROM dbo.ContentTagGROUP BY TagORDER BY 'Count' DESCI then created a stored procedure which would return me the top 60 tags ordered alphabetically:
SELECT * FROM vwTagCloud ORDER BY Tag
The UI
I decided to place the tag control within a UserControl incase I needed to include it in more than one page, in the end it was only used in the MasterPage.I created a method called GetCloud() which would contain the logic for the tag weighting. The method accepts a DataTable and a String based Cloud Template. The Cloud Template is tokenised, the replaced version is returned for each tag in the DataTable.
////// Return a tag could. /// ///
/// The template to return for each iten. Should include $tag$ and
/// $weight$ which will be replaced with actual data
/// <h$weight$><a href='#$tag$'>$tag$</a></h$weight$>
///
/// String containing tag cloud
private string GetCloud(DataTable tagData, string cloudTemplate)
{
Next I included some validation on the input table.
// Check the input data if (!tagData.Columns.Contains("tag")) throw new Exception("Expected column 'tag' is missing"); if (!tagData.Columns.Contains("count")) throw new Exception("Expected column 'count' is missing");
Now
time to get down to business. To figure out the weight of the tag I
first needed to know the Min and Max tag occurances. For this I used
DataTable.Compute(). You can see more information on the Compute()
method in my article about EvaluatingExpressions.
// Lets get cracking StringBuilder outputBuffer = new StringBuilder(); double max = 0; double min = 0; // Use Compute to get the min and max counts double.TryParse(tagData.Compute("min(count)", null).ToString(), out min); double.TryParse(tagData.Compute("max(count)", null).ToString(), out max);
With the Min and Max known I could loop through the DataTable figuring out the tag weight.
// Loop through the data, generate the tag cloud foreach (DataRow row in tagData.Rows) {
First
of all I needed to calculate the weight as a percentage. For this I
divided the number of occuances by the maximum occurances and multiplied
it by 100.
// work out the weight double weightPercent = (double.Parse(row["count"].ToString()) / max) * 100; int weight = 0;
With
the weight percentage I apply a bunch of cascading 'if' statements to
figure out which weight classification the tag belongs to
if (weightPercent >= 99) { //heaviest weight = 1; } else if (weightPercent >= 70) { weight = 2; } else if (weightPercent >= 40) { weight = 3; } else if (weightPercent >= 20) { weight = 4; } else if (weightPercent >= 3) { //weakest weight = 5; } else { // use this to filter out all low hitters weight = 0; }
Lastly I replace the tokens in the cloud template and append the result to a StringBuilder.
// don't return data for unweighted items if (weight > 0) outputBuffer.Append(cloudTemplate.Replace("$weight$", weight.ToString()).Replace("$tag$", row["tag"].ToString()).Replace("$urlencodetag$",HttpUtility.UrlEncode(row["tag"].ToString()))); }
When all are evaluated, return the StringBuilder
return outputBuffer.ToString(); }
CSS
Ok,
this code generates a bunch of Anchor tags. I'd need to use CSS to make
the thing look decent. The cloud template used the weight to assign an
appropriate style to each tag. The styles used are
- weight1
- weight2
- weight3
- weight4
- weight5
The following StyleSheet was created to bring these tags to life within a
called tagCloud
I hope my code makes your life a bit easier, if nothing else I hope it inspires you to add a cloud to your site... doesn't just have to be for
<style> #tagCloud { width:238px; border:solid 1px #ccc; padding:5px; margin-bottom:10px; text-align:justify; } #tagCloud A { text-decoration:none; margin-left:5px; margin-right:5px; font-family:Trebuchet MS, Verdana, Arial; text-transform:lowercase; } #tagCloud A:hover { color:#00cc00; text-decoration:underline; } #tagCloud A.weight1 { color: #ff9900; font-size: 1.9em; font-weight:bolder; } #tagCloud A.weight2 { color: #4169e1; font-size:1.5em; font-weight:bolder; } #tagCloud A.weight3 { color: #009eff; font-size: 1.4em; font-weight:bolder; } #tagCloud A.weight4 { color: #4188cf; font-size: 1.2em; } #tagCloud A.weight5 { color: #83bcd8; font-size: 1.0em; }style>
Adding my cloud to the UserControl
Html
The Html is really simple, as you can see below:<div id="tagCloud"> <asp:Literal ID="CloudLiteral" runat="server" /> >
Code Behind
The Page_Load() calls my DataAccessLayer to get the DataTable and passes the data to the local GetCloud() method. Note the use of ResolveClientUrlprotected void Page_Load(object sender, EventArgs e) { String browseUrl = Page.ResolveClientUrl("~/TagBrowse.aspx"); CloudLiteral.Text = GetCloud(ContentTag.GetCloudData().Tables[0], " + browseUrl + "?tag=$urlencodetag$'>$tag$ "); }
The Result
You can see the result of my efforts right here on GeekZilla, if you're interested in using or enhancing this code, feel free to download the example application linked from the bottom of this article.I hope my code makes your life a bit easier, if nothing else I hope it inspires you to add a cloud to your site... doesn't just have to be for
The Result
You
can see the result of my efforts right here on GeekZilla, if you're
interested in using or enhancing this code, feel free to download the
example application linked from the bottom of this article.
I hope my code makes your life a bit easier, if nothing else I hope
it inspires you to add a cloud to your site... doesn't just have to be
for tagsSource: www.geekzilla.co.uk
No comments:
Post a Comment