If you get your data model right, implementing a tagcloud turns out to be a snack. In Gravl, I’ve setup a many-to-many between BlogEntry and Tag, but I also have a link from the parent Blog directly to all available tags:

Data Model from IntelliJ

Implementing many-to-many simplifies to import process when I import my Pebble data. The import process ends up with something like this:

def tag = Tag.findByBlogAndName(newBlog, importedCat)
if (tag == null) {
    tag = new Tag(name: importedCat)
    log.debug "Creating new tag for ${importedCat}"
    // Grails handles the bidirectional link for us
    newBlog.addToTags(tag).save()
} else {
    log.debug "Found existing tag for ${importedCat}"
}
newEntry.addToTags(tag).save()

Once you’ve got an easy way to access all tags for a blog, and a count of how many times they occur, you’re all set to following a simple tag cloud algorithm. Sounds like a perfect case for a TagCloudTagLib? Needs some error handling, but here’s a first cut (apologies for the formatting):

class TagCloudTagLib {

    // Simple Algo from http://www.petefreitag.com/item/396.cfm
    def tagCloud = {attrs, body ->

        def blogId = attrs.blogId
        Blog blog = Blog.findByBlogid(blogId)
        log.debug "Building tagcloud for: $blog.title"

        // step 1: determine tags and their frequency
        def tagToFreq = new TreeMap()

        blog.tags.each {tag ->
            def tagCount = tag.entries.size()
            tagToFreq[tag.name] = tagCount
            log.debug "Setting size of ${tag.name} to $tagCount"
        }
        def allFreq = tagToFreq.collect {tag, freq -> return freq}.sort()
        def minFreq = allFreq[0]  // first entry
        def maxFreq = allFreq[-1] // last entry
        log.debug "Max is $maxFreq and min is $minFreq"

        // step 3: find the spread, use 5 font sizes
        def distrib = maxFreq - minFreq
        def catSize = (distrib / 5).intValue() + 1 // add 1 to simulate ceil()

        // step 4: output your tags
        tagToFreq.each {tag, freq ->
            out << "<a href='/$blogId/tags/$tag' class='tagCloudSize" + (freq / catSize).intValue() + "'>"
            out << tag
            out << "</a> "
            log.debug "Tag class of $tag with size $freq is " +  (freq / catSize).intValue()
        }

    }

}

Which make using tagclouds in a gsp as easy as:

<g:tagCloud blogId="glen"/>

Or something more dynamic..

Of course, you’ll want to style those bad boys once you put them in a GSP. But the CSS is pretty straightforward:

.tagCloudSize0 { font-size: xx-small; }
.tagCloudSize1 { font-size: small;  }
.tagCloudSize2 { font-size: medium;  }
.tagCloudSize3 { font-size: large;  }
.tagCloudSize4 { font-size: xx-large;  }

And the result? Well… I really need to add some more categories for my blogging… Or get a more balanced life:

Gravl TagCloud in action

If you’d like to have a more detailed browse of the source, I’m hosting at google code for now. Check out the source page. It’s all Apache2.

Ok… Tagclouds done… Now on to some calendaring…