woven maps

last christmas I offered a series of prints and plots as gifts to my family & relatives. for some of them, I used vector maps as a way to seed a graphic post-processor. it looked like this:

rue Lecourbe, Paris.


rue Pelleport, Paris.pics3

l’opéra de Paris.




Notre-Dame de Paris


these are the steps I followed:

  1. collect a set of points
  2. triangulate the set
  3. compute the triangles’ edges’ lengths
  4. draw the gradient. for a given height:
    1. translate the canvas vertically and give it a small opacity
    2. render only the edges which length is below a given threshold
  5. draw a given amount of extra wireframes as 4 with a higher opacity
  6. overlay an additive picture
  7. render white wireframes & glow

it may seem complicated but apart from the triangulation, it is mostly a canvas work.

the first 3 steps work together in a function called process(), the 4 last steps are grouped under render().

first the code of the process() function

1 collect a set of points

this is a variably trivial task, in the demo below I collect the X/Y corrdinates of the mouse/finger. for the actual pieces, I used the vector data of the Mapzen API, it could basically be anything in 2D. One restriction though, because of the next step, we need a set and not only a list ; the difference being that a set doesn’t contain duplicates. cleanup() is a method that removes duplicate points from a list.

2 triangulation

the heavy lifting, I used this delaunay triangulation lib, even though it’s not extra robust, it works in most cases.

3 compute the triangles’ edges’ lengths

again, not much to say, only storing the lengths of each edge and the 2 vertices they’re made of in an array to batch draw them without having to compute their length each time (computing edges’ lengths all the time is not necessary as they don’t change from a render to the next).

when process() is over, we call the render() method

steps 4 & 5

both work the same : the loops will translate the canvas by a certain amount, change the opacity and call the renderEdges() method many times.

renderEdges( edges, min )

for each computed edge, we check its length against a min value and, if the edge is shorter than the value, we draw it otherwise, we continue.

in the #4 loop, min is slowly incremented while in #5 it jumps from 10 to 10. the smaller the value of min, the denser the mesh, the bigger the value of min the sparser the mesh.

this does most of the work, the rest is a cosmetic layer:

6 overlay

this is a single context.drawImage() call with the context’s globalCompositeOperation set to “screen”. the 9 arguments of the drawImage call are: the image to draw, the 4 value of the source rectangle and the 4 values of the destination rectangle. I’m using a variety of images from this license free website and consequently downscale them. this is the one I use in the demo below pattern. as the image is very small and is stretched over a big area, the bilinear interpolation used by drawImage() prevents the end result from pixelating (it could be nice though…)

7 glow

the glow can be obtained by drawing 2 versions of the edges: one regular, the other with a wider strokeStyle & a blur filter.

here’s a demo if you want to try it out:

you can download a zip here and check the demo page here.
despite it’s simplicty and the limited amount of parameters, it’s quite a versatile process. here are some doodles obtained with the above demo.
t1 t2t3t0

and that was … no wait!

there are some prints for sale here https://society6.com/barradeau/collection/woven-maps if you would like a place to appear in the list, let me know :)

NOW that was it :)

Leave a Reply

Your email address will not be published. Required fields are marked *