0

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.

ryeballar
  • 29,658
  • 10
  • 65
  • 74

1 Answers1

1

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>
Robby Cornelissen
  • 91,784
  • 22
  • 134
  • 156
  • Thank you very much! I've been grinding myself with this problem for a couple of hours now. – ryeballar Jun 07 '21 at 06:37
  • 1
    One could also use `vector-effect: non-scaling-stroke;` but the best might be to instead scale the coordinates to more *natural* values, playing with the extremes like this makes it more prone to rounding errors and weird bugs. – Kaiido Jun 07 '21 at 06:39
  • @Kaiido `vector-effect: non-scaling-stroke` works as well. Unfortunately, it doesn't work on IE11 which is something that our company still supports. In regards to scaling it up to natural values, does multiplying the x,y points to a higher percentage work? Or are there other simpler ways to convert it? – ryeballar Jun 07 '21 at 06:44
  • Yes, multiplying your coords by a set factor will work, you'd have to calculate said factor, I personally don't have time to look at your code thoroughly to better guide you on how to do so, sorry. – Kaiido Jun 07 '21 at 06:54