Map Pins using SVG Path

Michael Rovinsky
Welldone Software
3 min readFeb 13, 2018

--

A Pin is a simple visual element used in many web applications. Here is an example similar to the Google Maps’ ones (click on the Result tab):

Since the pins usually point to other visual objects, it’s better to put a pin under a <g> container element. Position of the container is set with the transform attribute. Here is a D3 code to do it:

let container = d3.select('svg').append('g')
container.attr('transform', `translate (${x},${y})`)

Now we need to build the Pin’s path:

  1. Move to A: M aX,aY
  2. Line to B: L bX,bY
  3. Clockwise Arc to D (around C): A r,r 1 1 1 dX,dY
  4. Line to A: L aX,aY
  5. Close the path: z

Let’s assume the point A has zero coordinates {x: 0, y: 0}. The coordinates of the other points can be calculated by the shape’s height and radius:

function pinPath (height, radius) {
const dyAC = height - radius
const alpha = Math.acos(radius / dyAC)
const deltaX = radius * Math.sin(alpha)
const deltaY = height * (height - radius * 2) / dyAC
return `M 0,0
L ${-deltaX},${-deltaY}
A ${radius} ${radius} 1 1 1 ${deltaX},${-deltaY}
L 0,0 z`
}

… and another one adding a pin to a container:

function addPin(container, height, radius, background, border) {
const path = pinPath(height, radius)
return container
.append('path')
.attr('d', path)
.style('stroke', border)
.style('fill', background)
}

Another option to build a pin-shaped path is a little more sophisticated, but brings an elegant result:

That’s how we do it:

  1. Move to A: M aX,aY
  2. Cubic Bezier curve by the points B and C to A: C bX,bY cX,cY aX,aY
  3. Close the path: z

Now we need to compute the coordinates of A and B by the resulting shape’s width and height. A Cubic Bezier Curve (CBC) is based on 4 points and built by a parameter t where 0 ≤ t ≤ 1:

In our case, The first and the fourth points are the same (A), the second (B) and the third (C) are symmetric: bX = aX - ΔX; cX = aX + ΔX

Assuming aX = 0, we have a much simpler equation for x coordinate by t:

To get the minimum and maximum of X (points D and F), we need to find where the derivative of X (t) is equal to zero:

The maximum delta X is half of the resulting shape’s width. Thus:

The calculation of delta Y is simpler.Assuming aY = 0, bY = cY, and the highest point of the shape is exactly in the middle (t = 0.5):

Here is the function:

function pinPathCubicBezier(width, height) {
const deltaX = width * Math.sqrt(3)
const deltaY = height * 4 / 3
return `M 0,0 C ${-deltaX},${-deltaY}
${deltaX},${-deltaY} 0,0 z`
}

To learn more about SVG paths, please refer the official Mozilla tutorial. Have a fun!

--

--