Skip to content

SVG path with stroke fills the svg element

I have a set of coordinates that can be used to create a polygon in google maps. I used the mercantor-projection formula described in this answer to convert it into a set of points (x,y) that can be used in an svg . The conversion was successful and it was rendered properly.

The snippet below renders well without the setting the stroke property in the element. If you un-comment line 29 where it sets the stroke property to red, it will fill up the entire svg element instead of outlining the element with red which highlights the main problem of this post.

const COORDINATES = [[-86.917595,32.664169],[-86.918817,32.644278],[-86.898304,32.567687],[-86.90675,32.537298],[-86.890829,32.513742],[-86.878989,32.487323],[-86.885203,32.482438],[-86.870212,32.468974],[-86.860056,32.450861],[-86.86709,32.439188],[-86.849898,32.438325],[-86.824394,32.424725],[-86.845448,32.415416],[-86.843758,32.400416],[-86.827244,32.378816],[-86.815399,32.370821],[-86.807378,32.354356],[-86.814912,32.340803],[-86.820921,32.33324],[-86.816107,32.30997],[-86.798268,32.308632],[-86.773163,32.340728],[-86.780447,32.3686],[-86.778365,32.394601],[-86.749981,32.389105],[-86.727181,32.404497],[-86.717897,32.402814],[-86.719028,32.372059],[-86.711337,32.360767],[-86.683537,32.353395],[-86.655597,32.376147],[-86.653419,32.397247],[-86.618,32.405717],[-86.613453,32.398584],[-86.614841,32.374266],[-86.595335,32.361345],[-86.581873,32.375019],[-86.570551,32.375006],[-86.542537,32.363517],[-86.532531,32.338775],[-86.496774,32.344437],[-86.491902,32.364327],[-86.463564,32.377288],[-86.461277,32.403473],[-86.456273,32.405837],[-86.444721,32.399841],[-86.411172,32.409937],[-86.412229,32.528746],[-86.412446,32.579324],[-86.413116,32.707386],[-86.524434,32.707058],[-86.714219,32.705694],[-86.71339,32.661732],[-86.771532,32.660717],[-86.816574,32.660117],[-86.917595,32.664169]];

const getPoint = ([x, y]) => {
    return {
        x: (x + 180) * (256 / 360),
        y: (256 / 2) - (256 * Math.log(Math.tan((Math.PI / 4) + ((y * Math.PI / 180) / 2))) / (2 * Math.PI))
    };
};

const svg = document.querySelector('svg');
const points = [];
let minX = 256;
let minY = 256;
let maxX = 0;
let maxY = 0;

for(const coordinate of COORDINATES) {
  const point = getPoint(coordinate);
  minX = Math.min(minX, point.x);
  minY = Math.min(minY, point.y);
  maxX = Math.max(maxX, point.x);
  maxY = Math.max(maxY, point.y);
  points.push(`${point.x},${point.y}`);
}

const g = document
  .createElementNS("http://www.w3.org/2000/svg", 'g');

const path = document
  .createElementNS("http://www.w3.org/2000/svg", 'path');

svg.setAttribute(
  'viewBox', 
  [minX, minY, maxX - minX, maxY - minY].join(' ')
);

path.setAttribute('d', 'M' + points.join(' ') + 'z');
path.setAttribute('fill', 'blue');
//path.setAttribute('stroke', 'red');
  
g.appendChild(path);
svg.appendChild(g);
<svg height="400" width="400" preserveAspectRatio="xMinYMin meet"></svg>

Why does setting the stroke property fills the SVG element with the specified stroke color?

It would be great if anyone can point out how to solve the problem of the snippet above.

Answer

The problem is related to the scaling of your SVG. Setting the stroke width to a small value like 0.0025 fixes it.

const COORDINATES = [[-86.917595,32.664169],[-86.918817,32.644278],[-86.898304,32.567687],[-86.90675,32.537298],[-86.890829,32.513742],[-86.878989,32.487323],[-86.885203,32.482438],[-86.870212,32.468974],[-86.860056,32.450861],[-86.86709,32.439188],[-86.849898,32.438325],[-86.824394,32.424725],[-86.845448,32.415416],[-86.843758,32.400416],[-86.827244,32.378816],[-86.815399,32.370821],[-86.807378,32.354356],[-86.814912,32.340803],[-86.820921,32.33324],[-86.816107,32.30997],[-86.798268,32.308632],[-86.773163,32.340728],[-86.780447,32.3686],[-86.778365,32.394601],[-86.749981,32.389105],[-86.727181,32.404497],[-86.717897,32.402814],[-86.719028,32.372059],[-86.711337,32.360767],[-86.683537,32.353395],[-86.655597,32.376147],[-86.653419,32.397247],[-86.618,32.405717],[-86.613453,32.398584],[-86.614841,32.374266],[-86.595335,32.361345],[-86.581873,32.375019],[-86.570551,32.375006],[-86.542537,32.363517],[-86.532531,32.338775],[-86.496774,32.344437],[-86.491902,32.364327],[-86.463564,32.377288],[-86.461277,32.403473],[-86.456273,32.405837],[-86.444721,32.399841],[-86.411172,32.409937],[-86.412229,32.528746],[-86.412446,32.579324],[-86.413116,32.707386],[-86.524434,32.707058],[-86.714219,32.705694],[-86.71339,32.661732],[-86.771532,32.660717],[-86.816574,32.660117],[-86.917595,32.664169]];

const getPoint = ([x, y]) => {
    return {
        x: (x + 180) * (256 / 360),
        y: (256 / 2) - (256 * Math.log(Math.tan((Math.PI / 4) + ((y * Math.PI / 180) / 2))) / (2 * Math.PI))
    };
};

const svg = document.querySelector('svg');
const points = [];
let minX = 256;
let minY = 256;
let maxX = 0;
let maxY = 0;

for(const coordinate of COORDINATES) {
  const point = getPoint(coordinate);
  minX = Math.min(minX, point.x);
  minY = Math.min(minY, point.y);
  maxX = Math.max(maxX, point.x);
  maxY = Math.max(maxY, point.y);
  points.push(`${point.x},${point.y}`);
}

const g = document
  .createElementNS("http://www.w3.org/2000/svg", 'g');

const path = document
  .createElementNS("http://www.w3.org/2000/svg", 'path');

svg.setAttribute(
  'viewBox', 
  [minX, minY, maxX - minX, maxY - minY].join(' ')
);

path.setAttribute('d', 'M' + points.join(' ') + 'z');
path.setAttribute('fill', 'blue');
path.setAttribute('stroke', 'red');
path.setAttribute('stroke-width', '.0025');
  
g.appendChild(path);
svg.appendChild(g);
<svg height="400" width="400" preserveAspectRatio="xMinYMin meet"></svg>