Search code examples
d3.jsgeojsontopojsongeography

Cannot add capitals to geojson world map


I am using d3 to render a world map with clickable countries. The countries are from a geojson file, and I have no problems loading a particular continent, region or the entire world, using this site: https://geojson-maps.ash.ms/

However, the data from that site does not have any capitals in their geojson files. So instead I looked at https://www.naturalearthdata.com/, which clearly shows that one should be able to render a region with cities on it. I take their files and convert them to geojson via https://mapshaper.org/, but for some reason I ONLY get cities, as single dots against a blank background. There is not a single map I can find which provides a map of countries with capitals/cities inside country borders.

This leads me to wonder whether I am somehow supposed to combine a geojson file of countries with a geojson file of capitals, and render one on top of the other. In that case, how is that possible? Or is there some other solution? I attach the code where I render my world map (which works fine):

const data = await d3.json("world.geo.json")

var topology = topojson.topology({ foo: data });

const countries = topojson.feature(topology, topology.objects.foo).features

let targetCountries = countries
selectCountry()

svg.selectAll(".country")
    .data(countries) /* binder selectAll till enter() */
    .enter().append("path")
    .attr("class", "country")
    .attr("d", path)
    .on("mouseover", function (d) {
        d3.select(this).classed("targeted", true)
    })
    .on("mouseout", function (d) {
        d3.select(this).classed("targeted", false)
    })

Solution

  • I have a solution now, posting it here in case it helps anyone else:

    const data = await Promise.all([d3.json("world.geo.json"), d3.json("capitals_0yl.json")])
    
    var topology = topojson.topology({ foo: data[0], bar: data[1] });
    
    const countries = topojson.feature(topology, topology.objects.foo).features
    const cities = topojson.feature(topology, topology.objects.bar).features
    
    let combined = d3.merge([countries, cities])
    
    svg.selectAll(".country")
        .data(combined) /* binder selectAll till enter() */
        .enter().append("path")
        .attr("class", "country")
        .attr("d", path)
        .on("mouseover", function (d) {
            d3.select(this).classed("targeted", true)
        })
        .on("mouseout", function (d) {
            d3.select(this).classed("targeted", false)
        })