Putting SVGs On The Map
In the previous blog post I showed how to generate Scalable Vector Graphics (SVGs) of small circles packed inside a larger circle using JavaScript. In today's post I'm packing circles inside a map of the UK and Ireland.
The first thing we will need is a black and white map of the UK and Ireland. This is surprisingly difficult to find online, but after some intense search engine work I was able to get one:
A black and white image allows us to easily check whether a pixel in the image is land (black) or sea (white), using its RGB(Red, Green, Blue) values. White, RGB(255, 255, 255), pixels will have an average RGB of 255 whereas black, RGB(0, 0, 0), pixels will have an average RGB of 0.
The majority of the code is much the same as before, we just need to change how the circles are placed:
const image = document.querySelector("#uk_and_ireland");const LX = image.width;const LY = image.height;const canvas = figure.querySelector("canvas");canvas.width = LX;canvas.height = LY;const ctx = canvas.getContext("2d");ctx.drawImage(image, 0, 0);const imageData = ctx.getImageData(0, 0, LX, LY).data;function placeCircle(circles, radius, imageData, circleColours, LX, LY) {//The guard number: if we don't place a circle within this number of trials, we give up.let guard = 500;while (guard > 0) {// Pick a random position on the image.const cx = randomIntFromInterval(0, LX - 1);const cy = randomIntFromInterval(0, LY - 1);const index = (cx + cy * LX) * 4;const red = imageData[index + 0];const green = imageData[index + 1];const blue = imageData[index + 2];const average = (red + green + blue) / 3;const overlaps = circles.some(existingCircle =>overlapWith(cx,cy,radius,existingCircle.cx,existingCircle.cy,existingCircle.r));// The circle doesn't overlap with any other circles and its in the dark image area.if (!overlaps && average < 255 / 2) {const circle = {cx: cx,cy: cy,r: radius,colour: circleColours[randomIntFromInterval(0, 3)],};circles.push(circle);return;}guard -= 1;}}
We can use the half way point between 0 and 255 (128) to determine if a pixel is black or white. If the pixel is black and the circle doesn't overlap with any of the others then we place it otherwise we find a different location on the image.
The finished result (with 3000 circle place attempts!) looks pretty good:
Here's a smaller interactive version, so you can make your own:
As always all of the code is available on my GitHub.