Tom MacWright

tom@macwright.com

That little world

blurred

Yesterday I wrote about common misunderstandings around the GeoJSON format, and led the post with a color-changing little illustration of the earth. It’s a lot like the image I drew for geojson.xyz’s launch post: a take on educational materials for geology.

I started with just the idea of making a hybrid between illustration style and code-controlled, lightweight SVG images. So the path to implementing it was more a desire path, but here’s that path.

Drawing the image

The linework

This part is simple: I drew this sketch, taking hints from Fantastic Maps, a wonderful site about drawing fantasy maps. Jonathan splits his drawing between tablets, an iPad Pro, and paper, but I just have paper, so this was on paper. His advice for drawing mountain ranges was especially vital.

Preparing the image for vectorization

I drew the linework on watercolor paper, so there’s lots of texture in the image that makes it harder to vectorize, and the lines aren’t as bold as they should be.

The linework after being blurred and contrast-corrected

So I brought the image into Pixelmator and made it bolder by:

  • Using the Curves tool to increase contrast and push the paper’s texture into pure white
  • Ran a Gaussian Blur effect at 1% to get rid of tiny breaks in the lines
  • Used Curves again to increase contrast even more.

Vectorizing the image

The only tool I’ve found for vectorization of this sort is potrace. You give it a raster file as input and it creates a vector file of various types. potrace only likes very simple raster formats like BMP, and I had exported this image as a PNG, so I used Imagemagick to convert.

All together:

$ convert blurred.png blurred.bmp
$ potrace blurred.bmp -s

This produces blurred.svg.

Tagging the SVG

Next I want to make some parts of the SVG file identifiable, so that I can control their colors. I could open up the file and try to figure out which shapes are controllable, but even I’m not that masochistic.

In Sketch, I open up the SVG file, make minor adjustments, and then rename the layers I want to control. Layer names in Sketch are ids in SVG, so when I save the file, I’ll be able to find those layers with getElementById.

Optimizing the SVG

Sketch and potrace both don’t focus a lot on optimization of SVGs, and for my purposes I like to have embedded SVGs as one-liners. So I use SVGO to optimize the file, making sure to disable the rule that’ll remove unused IDs - I fully plan on using those IDs.

$ svgo blurred.svg -o colorable.svg --disable=cleanupIDs

That gives me this SVG output:

blurred

Controlling colors

All that’s left is a little bit of JavaScript to control the colors. This selects the named paths with getElementById, creates some methods to generate colors, and then generates those colors every 2 seconds, or for each click.

var land = [
  document.getElementById('colorable-land-a'),
  document.getElementById('colorable-land-b'),
  document.getElementById('colorable-land-c')
];
var sides = [
  document.getElementById('colorable-left'),
  document.getElementById('colorable-right')
];
var sea = [document.getElementById('colorable-sea')];
function limitedRand(offset) {
  return ((offset || 100) + (Math.random() * 100)).toFixed(0);
}
function getGreen() {
  return 'rgb(' + [limitedRand(), 230, limitedRand()] + ')';
}
function getBlue() {
  return 'rgb(' + [limitedRand(-10), limitedRand(), 230] + ')';
}
function getRand() {
  return 'rgb(' + [limitedRand(), limitedRand(), limitedRand()] + ')';
}
function applyColor(elems, generator) {
  elems.forEach(function(elem) { elem.setAttribute('fill', generator()); });
}
var clicked = false;
function randomize(e) {
  applyColor(land, getGreen);
  applyColor(sea, getBlue);
  applyColor(sides, getRand);
  if (!e) {
    if (!clicked) setTimeout(randomize, 2000);
  } else {
    e.preventDefault();
    clicked = true;
  }
}
randomize();
document.getElementById('drawing').addEventListener('click', randomize);
document.getElementById('drawing').addEventListener('selectstart', function (e) {
  e.preventDefault();
});

That’s it!

Well, not exactly it. That’s the version that made it onto the post, since it was nice and lightweight and flashy, but I love watercoloring - nothing lightens a mood like watercoloring. So I watercolored the physical version, and it looks like:

The watercolored version