Map Pins using SVG Path

Michael Rovinsky
Feb 13, 2018 · 3 min read

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

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!

Welldone Software

The leading full-stack consultancy. Creating amazing frontends and rock-solid backends using top notch technologies and practices. Visit us at https://welldone.software.

Michael Rovinsky

Written by

Welldone Software

The leading full-stack consultancy. Creating amazing frontends and rock-solid backends using top notch technologies and practices. Visit us at https://welldone.software.

More From Medium

More from Welldone Software

More from Welldone Software

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade