Current Status: Independent Consultant

As many of you know, my run at Wellington ended at the end of February. My contract wasn’t renewed for reasons beyond my control and that was that.

No more suits, sadly.*

One of the big problems with that contract ending was that the timing of it was a little abrupt. I found out just a couple of hours before I got on stage to speak at jQuery conference which was, in turn, just over two weeks away from what would turn out to be my end date. Since the time frame was compressed and I wasn’t really in the “new job” mindset to begin with, I ended up going into the first month of my job search with the wrong attitude. That wrong attitude was that it was okay to talk to people because, at the end of the day, I wasn’t working anyway, so why not hear people out?

It turns out that talking to as many people as I talked to in March is actually a pretty crummy idea. While I had good conversations with a few companies and actually got pretty close on a couple of roles that made some sense for me at this stage of my career I ended up burning myself out on the interview process before the month was over.**

In hindsight, I should have chosen my conversations more wisely, written a bunch and done a ton more open source work. Oops.

Anyway, in the back of my mind, as I was doing all of this interviewing and listening to everyone who wanted a moment of my time I was nurturing the thought that I would be happier just doing my own thing as an independent consultant for a while. I’ve always threatened to start my own agency and while this isn’t the way I imagined doing it, it is a way to do it, so the option was one that I’d thought about for a long time. It might not be my own version of Sterling Cooper Draper Pryce (even if I’ve been know to dress like a modern Don Draper), but it’s still an opportunity to help clients with tough problems and do so under my own banner. That’s pretty cool by itself and if it turns into something bigger down the road, then even better. And if it doesn’t work out? That’s cool too. At least I gave it a shot.

So, to make a long story short, unless someone blows me away with a great role I’m going to be a sword-for-hire for the foreseeable future. Sure, I will listen to people that have really great engineering leadership roles, but I’m going to focus more on doing consulting and training on my own. To start, as I’ve already got an engagement that will keep me busy for the next few weeks, I’ve got a small page outlining the services I provide. I’ll be creating a more official presence as time allows, but for now that page is good enough to give you a sense of what I’m available to do.

So, hire me if you’ve got the need.


* Stated without irony. I like the suits.

** I actually had some help on that, but more on that in a subsequent post. I’ve got a couple of anecdotes to share about the interview process as I’ve witnessed it, but that will come in the next couple of weeks.

I Made a Scatter Plot Data Visualization with AngularJS and SVG. Here’s How I Did It.

This post is a longer examination of the new scatter plot visualization I added to my upcoming data visualization presentation for jQuery Conference. As with the other Angular examples I’ve posted recently, this one is based on the data I keep about the world’s most valuable comics. This particular visualization lives on the page I keep detailing record comic book sales. Take a look. It’s pretty cool, especially if you’re fascinated by comic books that have sold for more than $100,000.

For reference, it looks like the following. It’s 20 years of comics sales plotted horizontally with their sale prices (ranging from $100,000 to $2,500,000) plotted vertically. You can see a couple of clusters of activity based on different events in the marketplace.

chart

So, how did I do it? And why did I do it this way? D3 actually has a few scatter plot examples out there, so I could have used one of those, but as I thought about it I decided I wanted to do something hand-rolled. A scatter plot is actually pretty easy to map out and, as an added bonus, I could lay out the basics of the chart in Illustrator, which allowed me to demo the fact that SVG can be authored like any image by a graphic designer and then manipulated with JavaScript and CSS.

So let’s take a look at the particulars.

First we’ll start with the markup. This is Angular, after all. I’ve simplified the markup a bit, taking out some of the elements representing grid lines. Check the source on Github for all the details.

The interesting bits, from the Angular perspective, happen around line 30. The core of the visualization is in the circle element. It’s an ng-repeat that goes through the items collection in the controller’s scope. It’s filtered by the venues property.

Take note of the prefixes on the next set of attributes. For starters, as I normally do, I prepend data-* to Angular attributes as that’s the way custom attributes are defined in the spec. I like to play by the rules. You’ve probably seen that before. What’s probably not so familiar is the use of the additional ng-attr-* prefix. If you’re familiar with Angular you know you should just be able to do fill="{{colorPicker(it.venue)}}" and Angular should evaluate the results of the colorPicker method and insert it in as the value of the fill attribute. This doesn’t work with SVG elements. Some browsers will parse the entire SVG element before Angular has a chance to evaluate the SVG attributes. So if you put n Angular expression inside an SVG attribute it will throw an error. ng-attr solves this by binding the attribute solely to Angular’s discretion. Here, I’m using this pattern three times. The fill attribute, which provides the color coding for the different venues s these comic books have sold (with the handy colorPicker method that accepts the it.venue as an argument) , and the cx and cy attributes which indicate the center of the circle.

The cx and cy attributes are passed through custom filters which transform the data into usable x and y coordinates. I’ll talk about those in the section on the Angular filters. Following the filters there are two mouse events used to show and hide a simple (svg) tooltip.

The tooltip is in the next section. Doing boxes in SVG isn’t quite the same thing it is with HTML. You can’t just throw a box in there and have text flow inside the box like you can with a DIV or something, so this would have been easier with an HTML tooltip. That said, I wanted to do this in SVG so it’s all in SVG. In this case I’m doing it with a solid box with several text elements layered on top. All of these tooltip elements show based on the tooltip model on the scope. If the price is truthy, the tooltip shows. Otherwise, it’s hidden. For each text element and the rect element, I’m using x and y coordinates generated with the same Angular filters in use on the circles and using the transform attribute to move the element around based on that common x/y. Each of the text elements also expose the data from the model.

Following the tooltip code, in the ‘legend’ g element there are several rect elements which allow for quick filtering by sales venue. ng-mouseover and ng-mouseout are used to update the venues filter.

That’s the markup. SVG isn’t that scary, is it?

 <div data-ng-app="comicsApp">
  <div data-ng-controller="chartCtrl"> 
    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
    width="980" height="600" viewBox="0 0 980 600" xml:space="preserve">
      <g id="lines">
        <line fill="none" stroke="#000000" stroke-miterlimit="10" 
         x1="90.828" y1="-8" x2="90.828" y2="579.297"/>
        <line fill="none" stroke="#B5B5B6" stroke-miterlimit="10" 
         x1="0" y1="579.297" x2="981.996" y2="579.297"/>
        <line fill="none" stroke="#595858" stroke-miterlimit="10" 
         x1="734.275" y1="49.246" x2="734.275" y2="579.161"/>
        <!--about 50 lines cut-->
      </g>
      <g id="text">
        <text x="10" y="16">price in USD</text>
        <text x="71" y="574">$0</text>
        <text x="480" y="595">2003</text>
        <text x="78" y="595">1993</text>
        <text x="881" y="595">2013</text>
        <text x="25" y="553">$100,000</text>
        <text x="25" y="468">$500,000</text>
        <text x="16" y="363">$1,000,000</text>
        <text x="16" y="256">$1,500,000</text>
        <text x="16" y="151">$2,000,000</text>
        <text x="16" y="45" >$2,500,000</text>
      </g>
      <g id="dynamic">
        <circle  data-ng-repeat="it in items | filter : venues" 
         opacity="0.8" 
         data-ng-attr-fill="{{colorPicker(it.venue)}}" 
         data-ng-attr-cx="{{it.date | xDate}}"
         data-ng-attr-cy="{{it.price | yPrice}}" 
         data-ng-mouseover="updateTooltip(it)" 
         data-ng-mouseout="updateTooltip(0)" r="7" />
        <rect data-ng-show="tooltip.price" data-ng-model="tooltip" 
         transform="translate(-55,-100)" 
         data-ng-attr-x="{{tooltip.date | xDate}}" 
         data-ng-attr-y="{{tooltip.price | yPrice}}" 
         width="150" height="80" 
         fill="white" style="filter:url(#drop-shadow)" />
        <text data-ng-show="tooltip.price" data-ng-model="tooltip" 
         transform="translate(-45,-90)" 
         data-ng-attr-x="{{tooltip.date | xDate}}" 
         data-ng-attr-y="{{tooltip.price | yPrice}}">
         {{tooltip.title}} {{tooltip.issue}}</text>
        <text data-ng-show="tooltip.price" data-ng-model="tooltip" 
         transform="translate(-45,-75)" 
         data-ng-attr-x="{{tooltip.date | xDate}}" 
         data-ng-attr-y="{{tooltip.price | yPrice}}" >
         {{tooltip.pedigree}}{{tooltip.collection}}{{tooltip.provenance}} {{tooltip.grade_src | srcFilter}} {{tooltip.grade}}</text>
        <text data-ng-show="tooltip.price" data-ng-model="tooltip" 
         transform="translate(-45,-60)" 
         data-ng-attr-x="{{tooltip.date | xDate}}" 
         data-ng-attr-y="{{tooltip.price | yPrice}}" >
         {{tooltip.price|currency}}</text>
        <text data-ng-show="tooltip.price" data-ng-model="tooltip" 
         transform="translate(-45,-45)" 
         data-ng-attr-x="{{tooltip.date | xDate}}" 
         data-ng-attr-y="{{tooltip.price | yPrice}}" >
         {{tooltip.date}}</text>
        <text data-ng-show="tooltip.price" data-ng-model="tooltip" 
         transform="translate(-45,-30)" 
         data-ng-attr-x="{{tooltip.date | xDate}}" 
         data-ng-attr-y="{{tooltip.price | yPrice}}" >
         {{tooltip.venue}}</text>
      </g>
      <g id="legend">
        <rect x="125" y="5" width="15" height="15" fill="#D95B43" 
         data-ng-mouseover="venues='comic connect'" 
         data-ng-mouseout="venues=''"></rect>
        <text x="145" y="15" class="legend">Comic Connect</text>
        <rect x="245" y="5" width="15" height="15" fill="#ECD078"  
         data-ng-mouseover="venues='heritage'" 
         data-ng-mouseout="venues=''"></rect>
        <text x="265" y="15" class="legend">Heritage</text>
        <rect x="325" y="5" width="15" height="15" 
         fill="#C02942" data-ng-mouseover="venues='comiclink'" 
         data-ng-mouseout="venues=''"></rect>
        <text x="345" y="15" class="legend">ComicLink</text>
        <rect x="405" y="5" width="15" height="15" fill="#542437" 
         data-ng-mouseover="venues='pedigree'" 
         data-ng-mouseout="venues=''"></rect>
        <text x="425" y="15" class="legend">Pedigree</text>
        <rect x="485" y="5" width="15" height="15" fill="#53777A" 
         data-ng-mouseover="venues='metropolis'" 
         data-ng-mouseout="venues=''"></rect>
        <text x="505" y="15" class="legend">Metropolis</text>
        <rect x="565" y="5" width="15" height="15" fill="#69D2E7" 
         data-ng-mouseover="venues='jp'" 
         data-ng-mouseout="venues=''"></rect>
        <text x="585" y="15" class="legend">JP/Mint</text>
        <rect x="645" y="5" width="15" height="15" fill="#FA6900" 
         data-ng-mouseover="venues='mastronet'"  
         data-ng-mouseout="venues=''"></rect>
        <text x="665" y="15" class="legend">Mastronet</text>
        <rect x="725" y="5" width="15" height="15" fill="#FE4365" 
         data-ng-mouseover="venues='pgc'" 
         data-ng-mouseout="venues=''"></rect>
        <text x="745" y="15" class="legend">PGCMint</text>
        <rect x="805" y="5" width="15" height="15" fill="#666666" 
         data-ng-mouseover="venues='unknown'" 
         data-ng-mouseout="venues=''"></rect>
        <text x="825" y="15" class="legend">Other/Unkown</text>
      </g>
    </svg> 
  </div>
</div>

Now let’s take a look at the JavaScript. First up is the controller. This is pretty simple as far as these things go. $scope.items is first populated with data provided by an Angular factory. The factory is a simple $http.get so I’m not showing it here. I just organized the data request into a factory so two controllers could share the same code for getting comics data. Following that, the $scope.tooltip model is set with a single property, price set to zero. This falsy value ensures that the tooltip we previously defined in the HTML isn’t shown by default. Following that is a small method designed to update the tooltip model with data representing the currently selected comic book. Finally, there’s a small method and related array used to choose colors for the different venues. The method accepts a string argument and matches against a list of known venues, returning the proper color.

That’s the whole controller.

angular.module('comicsApp.controllers', ['comicFilters','comicsFactories']). 
  controller('chartCtrl', ["$scope",'dataService',function( $scope, dataService )  {
    $scope.items = dataService;
    $scope.tooltip = {
      price:0
    }
    $scope.updateTooltip = function(it) {
      $scope.tooltip = {
        price:it.price || 0,
        venue:it.venue,
        date:it.date,
    	 	title:it.title,
    		issue:it.issue,
    		pedigree:it.pedigree,
    		collection:it.collection,
    		provenance:it.provenance,
    		grade_src: it.grade_src,
    		grade : it.grade
      } 
    }
    $scope.colorPicker= function( venue ){
      switch (venue) {
        case "Heritage":
          return $scope.colors[0];
        case  "Comic Connect":
          return $scope.colors[1];
        case "Comiclink":
          return $scope.colors[2];
        case  "Pedigree":
          return $scope.colors[3];
        case "Metropolis":
          return $scope.colors[4];
        case  "JP The Mint":
          return $scope.colors[5];
        case "Mastronet":
          return $scope.colors[6];
        case  "PGCMint":
          return $scope.colors[7];
        default:
          return $scope.colors[8];
       }
    }
    $scope.colors = ["#ECD078","#D95B43","#C02942","#542437","#53777A","#69D2E7","#FA6900", "#FE4365","#666666"];
  }
]);

Now let’s take a look at the filters. As I mentioned earlier, this particular visualization isn’t generic, so these are only useful in the context of this particular implementation. Which is fine. I’m not doing a million of these.

The first, xDate, defines the x axis. It takes the date (in the form yyyy-mm-dd) and splits it into and array containing the years, months and days. I calculate the total number of months from 1993 by subtracting 1993 from the year and then multiplying that number by 12. I then adding that number to the number of months from the original date array. After that the numbers of months is fudged into a pixel measurement as the offset of the left of the chart, 90, is added to the number of months multiplied by 3.3333, which is a rough “month” unit on this particular chart.

The y axis filter is easier. It calculates a pixel representation of the book’s value (the value divided by 4700, the number of dollars per pixel) and that number is subtracted from 579, which is the offset from the top of the SVG element to the baseline of the chart.

If you were doing a generic chart that could take any similar data and produce a scatter plot, you would have to do these calculations on the fly. Getting the minimum and maximum values out of the data and creating a range and scales depending on the specific data. Since I defined the data and parameters for this before I wrote a line of code.

Yay for lazy.

angular.module('comicFilters', []).filter('xDate', function () {
    "use strict";
    return function (input) {
        if (input !== undefined) {
          var date = input.split("-");
          var years = date[0] - 1993;
          var months = (years * 12) + parseInt(date[1]);
          return 90 + months * 3.3333;
          
        }
    };
}).filter('yPrice', function () {
    "use strict";
    return function (input) {
      if (input !== undefined){
        return 579 - (input/4700);
      }
    };
})

And there you have it. Check out the $100,000 club repo for all the source code from this visualization and keep your eyes peeled for my jQuery Conference presentation where I’ll be talking about this as well as D3, the canvas element (with the web audio API) and Google Maps.

I’m Going to be Presenting at jQuery Conference. It’s in San Diego. You Should Go, Too.

It was announced last week, so I can now share the fact that I’m going to presenting on Data Visualization in the Browser at jQuery Conference in February. Because it’s a new year and because the presentation time is so tight (30 minutes!) I’m going to redo the existing dataviz presentation I’ve been giving entirely. I’ve got a new branch and everything.

I’m thinking of using World Bank data. At minimum, I’ll be doing something interesting and meaningful and, at best, I might end up on the World Bank data visualization tumblr.

I’m also super happy to be going to San Diego in February.

Late Notice: I’m Presenting on Canvas Tomorrow at HTML5 Boston

I tweeted about it but I never added it my public calendar and… never posted about it here. Anyway, I’m going to present on canvas tomorrow night at HTML5 Boston. It will be great.

It’s also at the Arsenal complex (at Harvard Business Review), which means I’ll be having Isobar flashbacks. It also means I’ll be running the western end of the Charles River for the first time in a while. Super fun.

Here are the slides.

I’m Giving Away a Copy of My Book, Beginning HTML and CSS

beginning-html-css

Want a free copy of my book? Sure you do. This month, I’m giving away a copy of my book right here on the blog (on this very post.)

The rules of this contest are simple, simply share an interesting/funny anecdote about your experience interviewing for a technology or agency position (no names or companies needed, blind items are fine.) At the end of October, I’ll pick the best one and you get a free copy of my book, Beginning HTML and CSS. I can’t tell you what kind of story will win, but if you’ve got an interview story that’s funny, creative, cool, inspirational, or whatever share it and you might get some free stuff. This contest is open to anyone in the world with an address. If I can send you a copy of my book using the US Postal Service, you’re eligible.

And heck, even if you’re already an expert and don’t have need of a beginner book, you can certainly hand it off to someone looking to make their own site for the first time or to an engineer from another technology discipline looking to get introduced to the joys of web technology.