7

I am trying to optimize a little bit the SVG I am generating using the amazing D3js geo module.

I am using d3.geo.path as the d attribute generator for SVG paths:

path = d3.geo.path().projection(config.projection); // projection is initialized somewhere else

and then using it to render paths like this:

svg.selectAll(".country")
     .data(countries)
     .enter()
     .insert("path", ".graticule")
     .attr({
         class: "country",
         d: path
     })

and the kind of path strings I am getting are like this one:

M713.601085,459.8780053259876L714.7443994399441,460.08170562468473L715.0310281028103,460.5903728431771L715.0310281028103...

As can be seen, some numbers are extremely long with too many decimals and not really useful at my current resolution, which clogs a little bit the DOM and makes it sluggish to debug these paths (there are also many, since it's a world map with lots of countries drawn on it).

So my first approach was creating this pathSimplified wrapper for path:

// Path simplified: take some decimals out of the 'd' string
pathSimplified = function (d) {
        return path(d).replace(/(\.\d{4})\d+/g, '$1');
    };

and then use it instead of path:

    //...
    .attr({
         class: "country",
         d: pathSimplified
     })

That works and now I get only 4 decimals for each value on the path string. Like this:

M713.6010,459.8780L714.7443,460.0817L715.0310,460.5903L715.0310...

My question is: can it be done in a better, less hackish way? It doesn't feel right to tweak the string after it's spit out by D3js's path function and it would be nice to specify the rounding at some point on the path itself or the projection...

User0123456789
  • 760
  • 2
  • 10
  • 25
  • 1
    In my case I chose a virtual SVG width (viewBox) big enough to show all the detail I need and then I removed the decimals entirely: `const path_trunc = d => path(d).replace(/\.\d+/g, '')` But I was wondering too whether geo.path had this functionality hidden somewhere. – Tobia Nov 24 '17 at 10:24

1 Answers1

4

Your idea is good, simplifying a polyline helps, but you can certainly do it better than just truncating coordinates: algorithms for polyline semplification exist and might give you nicer results.

For example, take a look at the Ramer-Douglas-Peucker algorithm. It is implemented in libraries like Simplify.js(check the demo on that page out), which also allows you to adjust it with tolerance. You'll have to pass the line as an array of x/y coords, but I trust that isn't too complicated to do.

I also noticed that d3 geo projections allow for a precision parameter (like here), maybe there's something like that that you overlooked? In any case, simplifying coordinates on your own with simplify.js is usually always possible.

Vale
  • 1,912
  • 16
  • 22
  • Thanks for the info, I will look into the precision parameter in d3.geo. Anyway, note that what I need is not simplifying the actual polygon, since that is already sorted out using the `topojson` utility (All info here: https://bost.ocks.org/mike/map/). My intention is merely cutting down decimal digits for the sake of DOM's verbosity. Therefore the need to control the output of the `path` function. – Óscar Gómez Alcañiz Mar 31 '16 at 15:54
  • 1
    @ÓscarGómezAlcañiz Well, ideally there are [adaptive resampling](http://bl.ocks.org/mbostock/3795544) algorithms that will also reduce the number of points, withouth losing too many details. If you just need to truncate, then your solution is fine already, the only thing that can make it better is if d3.geo actually allows to lower the precision of the output. – Vale Mar 31 '16 at 15:59