Search code examples
javascriptd3.jssvggeojsontopojson

How to get SVG path data of TopoJSON feature without adding it to the DOM?


I have a special case for which I do not want to add my generated path of a topojson file to the DOM, but rather only get the generated d attribute (the SVG path).

So I usually did something like this:

let features = feature(landmass, landmass.objects.land).features

        let projection = d3.geoAzimuthalEqualArea()
            .center([180, -180])
            .fitSize([width, height], { type: "FeatureCollection", features: features })

        let path = d3.geoPath().projection(projection)

        g.selectAll("#landmass")
            .data(features)
            .enter().append("path")
            .attr("id", "landmass")
            .attr("d", path);

But I actually just want the features to be translated to the path using the chosen geoPath, without adding any svg object to the DOM.

How can I achieve this?


Solution

  • D3's docs on the geographic path generators have you covered (emphasis mine):

    The geographic path generator, d3.geoPath, is similar to the shape generators in d3-shape: given a GeoJSON geometry or feature object, it generates an SVG path data string or renders the path to a Canvas.

    You can pass a GeoJSON feature into the path generator and have it create the path data—i.e. the path commands—which are then assigned to the d property of the <path> element. This is basically what happens when coding

    .data(features)
    // ... enter, append, etc...
    .attr("d", path); 
    

    This statement can be rewritten as:

    .data(features)
    // ... enter, append, etc...
    .attr("d", d => path(d));
    

    or, even more explicitly:

    .data(features)
    // ... enter, append, etc...
    .attr("d", feature => path(feature));
    

    Looking at the latter code snippet it becomes clear that D3—via its data binding means—creates a <path> element for every feature and passes that feature, i.e. the datum bound the the <path> element, to the path generator path to create the d property's path command string.

    With this knowledge you can easily create the path data strings for all your features without creating any DOM nodes:

    const pathData = features.map(feature => path(feature));
    

    More concisely, you can just pass the generator function to .map():

    const pathData = features.map(path);