0

I am new to d3.js. Trying to understand the cartogram example give in http://prag.ma/code/d3-cartogram/ . Here they gave example for USA map. I am trying the same for World Map to see how things works. My cartogram map has lines in between. My data has values for only few countries so I am setting the rest of the country's value as low or 0.

<!DOCTYPE html>
<html>
<head>
    <title>Cartograms with d3 &amp; TopoJSON</title>
    <meta charset="utf-8">
    <meta property="og:image" content="placeholder.png">
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script src="lib/colorbrewer.js"></script>
    <script src="lib/topojson.js"></script>
    <script src="cartogram.js"></script>
    <style type="text/css">

        body {
            font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
            font-size: 14px;
            line-height: 1.4em;
            padding: 0;
            margin: 0;
        }

        #container {
            width: 960px;
            margin: 20px auto;
        }

        h1 {
            font-size: 200%;
            margin: 0 0 15px 0;
        }

        h2 {
            font-size: 160%;
            margin: 0 0 10px 0;
        }

        p {
            margin: 0 0 10px;
        }

        form, form > * {
            margin: 0;
        }

        #status {
            color: #999;
        }

        #map-container {
            height: 700px;
            text-align: center;
            position: relative;
            margin: 20px 0;
        }

        #map {
            display: block;
            position: absolute;
            background: #fff;
            width: 100%;
            height: 100%;
            margin: 0;
        }

        path.state {
            stroke: #666;
            stroke-width: .5;
        }

        path.state:hover {
            stroke: #000;
        }

        form {
            font-size: 120%;
        }

        select {
            font-size: inherit;
        }

        #placeholder {
            position: absolute;
            z-index: -1;
            display: block;
            left: 0;
            top: 0;
        }

    </style>
</head>
<body>
<div id="container">
    <h1>Cartograms with d3 &amp; TopoJSON</h1>
    <form>
        <p>
            <label>Scale by <select id="field"></select></label>
            <span id="status"></span>
        </p>
    </form>
    <div id="map-container">
        <svg id="map"></svg>
    </div>


</div>
<script>


    var margin = 1,
            width = 970 - margin,
            height = 700 - margin;

    if (!document.createElementNS) {
        document.getElementsByTagName("form")[0].style.display = "none";
    }
   var percent = (function() {

                var fmt = d3.format(".2f");
                return function(n) { return fmt(n) + "%"; };
            })(),
            fields = [
                {name: "(no scale)", id: "none"},
                {name: "Internet_Users", id: "internet", key: "Internet_Users", format : percent},
                {name: "GDP", id: "gdp", key: "GDP"},
                {name: "Literacy_rates", id: "literacy", key: "Literacy_rates", format : percent},
                {name: "female_male", id: "fm", key: "female_male"},
                {name: "Population", id: "pop", key: "Population"},
            ],

            fieldsById = d3.nest()
                    .key(function(d) { return d.id; })
                    .rollup(function(d) { return d[0]; })
                    .map(fields),
            field = fields[0],
            colors = colorbrewer.RdYlBu[3]
                    .reverse()
                    .map(function(rgb) { return d3.hsl(rgb); });

    var body = d3.select("body"),
            stat = d3.select("#status");

    var fieldSelect = d3.select("#field")
            .on("change", function(e) {
                field = fields[this.selectedIndex];
                location.hash = "#" + [field.id]
            });

    fieldSelect.selectAll("option")
            .data(fields)
            .enter()
            .append("option")
            .attr("value", function(d) { return d.id; })
            .text(function(d) { return d.name; });

    var map = d3.select("#map").attr("width", width + margin)
                    .attr("height", height + margin),
            zoom = d3.behavior.zoom()
                    .translate([-38, 32])
                    .scale(.95)
                    .scaleExtent([0.5, 10.0])
                    .on("zoom", updateZoom),
            layer = map.append("g")
                    .attr("id", "layer"),
            states = layer.append("g")
                    .attr("id", "states")
                    .selectAll("path");

    updateZoom();

    function updateZoom() {
        var scale = zoom.scale();
        layer.attr("transform",
                "translate(" + zoom.translate() + ") " +
                "scale(" + [scale, scale] + ")");
    }

    var proj = d3.geo.mercator().scale(145).translate([width / 2, height / 1.5]),
            topology,
            geometries,
            rawData,

            dataById = {},
            carto = d3.cartogram()
                    .projection(proj)
                    .properties(function(d) {
                        return dataById[d.id];
                    })
                    .value(function(d) {
                        return +d.properties[field];


                    });

    window.onhashchange = function() {
        parseHash();
    };

    d3.json("data/world_countries_topo.json", function(topo) {
        topology = topo;
        //  console.log("T",topology)
        geometries = topology.objects.countries.geometries;

        d3.csv("data/parallel_score.csv", function(data) {
            rawData = data;
            dataById = d3.nest()
                    .key(function(d) { return d.Id; })
                    .rollup(function(d) { return d[0]; })
                    .map(data);
            init();
        });
    });

    function init() {
        var features = carto.features(topology, geometries),
                path = d3.geo.path()
                        .projection(proj);

        states = states.data(features)
                .enter()
                .append("path")
                .attr("class", "state")
                .attr("id", function(d) {
                    return d.Id;
                })
                .attr("fill", "#000")
                .attr("d", path);

        states.append("title");

        parseHash();
    }

    function reset() {
        stat.text("");
        body.classed("updating", false);

        var features = carto.features(topology, geometries),
                path = d3.geo.path()
                        .projection(proj);

        states.data(features)
                .transition()
                .duration(750)
                .ease("linear")
                .attr("fill", "#fafafa")
                .attr("d", path);

        states.select("title")
                .text(function(d) {

                    return d.Id;
                });
    }

    function update() {
        var start = Date.now();
        body.classed("updating", true);

        var key = field.key

        var  fmt = (typeof field.format === "function")
                        ? field.format
                        : d3.format(field.format || ","),
                value = function(d) {
                    if(d.properties == undefined){}

                    else {
                       return +d.properties[key];
                    }
                },
                values = states.data()
                        .map(value)
                        .filter(function(n) {
                            return !isNaN(n);
                        })
                        .sort(d3.ascending),
                lo = values[0],
                hi = values[values.length - 1];
        console.log("L",lo)
        console.log("H",hi)

        var color = d3.scale.linear()
                .range(colors)
                .domain(lo < 0
                        ? [lo, 0, hi]
                        : [lo, d3.mean(values), hi]);

        // normalize the scale to positive numbers
        var scale = d3.scale.linear()
                .domain([lo, hi])
                .range([1, 1000]);

        // tell the cartogram to use the scaled values
        carto.value(function(d) {
            if( value(d) == undefined) {
                return lo
            }

            else {
                console.log("SCale", (value(d)))
                return scale(value(d));
            }
        });

        // generate the new features, pre-projected
        var features = carto(topology, geometries).features;

        // update the data
        states.data(features)
                .select("title")
        /*.text(function(d) {
         return [d.properties.Id, fmt(value(d))].join(": ");
         });*/

        states.transition()
                .duration(750)
                .ease("linear")
                .attr("fill", function(d) {
                    if(d.properties == undefined){
                        return color(lo)
                    }
                    else {

                        return color(value(d));
                    }
                })
                .attr("d", carto.path);

        var delta = (Date.now() - start) / 1000;
        stat.text(["calculated in", delta.toFixed(1), "seconds"].join(" "));
        body.classed("updating", false);
    }

    var deferredUpdate = (function() {
        var timeout;
        return function() {
            var args = arguments;
            clearTimeout(timeout);
            stat.text("calculating...");
            return timeout = setTimeout(function() {
                update.apply(null, arguments);
            }, 10);
        };
    })();

    var hashish = d3.selectAll("a.hashish")
            .datum(function() {
                return this.href;
            });

    function parseHash() {
        var parts = location.hash.substr(1).split("/"),
                desiredFieldId = parts[0],

                field = fieldsById[desiredFieldId] || fields[0];

        fieldSelect.property("selectedIndex", fields.indexOf(field));

        if (field.id === "none") {

            reset();

        } else {


            deferredUpdate();
            location.replace("#" + [field.id].join("/"));

            hashish.attr("href", function(href) {
                return href + location.hash;
            });
        }
    }

</script>
</body>
</html>

Here is the link of my map: My Map

Can Someone please explain me why I am getting this line.

Thanks.

  • Can you get your code into a [JSFiddle](http://jsfiddle.net/) and I'll try to have a play about with it? – figurine Apr 22 '15 at 07:46
  • check you row data has everything that you need ... i think it's path that get some wrong values and turns in to line because when in path we get some wrong in middle it will connect to near point ...and that turns in to line here ... try to use some another json for world map or use d3.js default topology for world map and check ... – Amit Rana Apr 22 '15 at 10:05
  • @AmitRana Yeah. There is something wrong with the topojson file. Thanks for the note. But without any cartogram, the map is displaying properly, I mean there is no lines in between. When I try to do cartogram based on the data, the lines are appearing. –  Apr 22 '15 at 16:39
  • Modified one : https://gist.github.com/shaliniravi/6de5e2c00e6539b3112e –  Apr 22 '15 at 16:51
  • Can someone please tell me how can I remove the lines ? Do I need to change the topoJSON ?? –  Apr 22 '15 at 17:00
  • @AmitRana I tried changing the topo json which has no lines like previous. But now My map just shows different color no cartogram. This is gist link for the modified one : https://gist.github.com/shaliniravi/6eb5a397e96abfdf9642 and the map https://04478727369367745762.googlegroups.com/attach/5b7a157e5af8c290/Capture.PNG?part=0.1&view=1&vt=ANaJVrH3MTzj3t5fzCRWMacy41yGWkC4WSaib5eAdKzi535_8OIoHV7Gb5iWwXGXPMIgN3LcY7B_lZK23WE4M7ZpNwsdKSJqXJp2iacGPb-afzslIkkIxig –  Apr 22 '15 at 21:55
  • OK let me see that and will get back to you .... now if your map have colors that is good but now we have to make sure why Cartograms effect that change shape is not there – Amit Rana Apr 23 '15 at 11:50
  • @AmitRana Its is weird when I use my old JSON file (world_countries_topo.json) I am getting cartogram shapes but the issue is the map (Russia) has lines in between. When I tried with new json (word.json) , the map has proper boundaries with colors but no carogram shapes. –  Apr 23 '15 at 17:06

1 Answers1

0

We had the same problem a year or so back and it is due to the arcs in the topojson file moving from 180 or 360 back to 0, basically wrapping at the ends of the map.

We needed to manually go into the map file and edit it using QGIS.

This resolved the issue of the lines.

You will also find if you are using the Cartogram code that the map of the world is far to detailed than you will need given you will be distorting the map anyway. If you are generating the cartogram in real time then you will face delays in the code.

You should probably reduce the complexity of the map too.

Here is an example of the JSON we used to create real time hexagonal cartograms in the browser.

Simplified World Map JSON

  • Thanks Ben. So the only way to solve this issue is to modify the Topo JSON File manually ?? –  Apr 24 '15 at 01:37
  • I did try with different JSON (world.json). My map has proper boundary now but the cartograms are not appearing just the colors are changing. https://gist.github.com/shaliniravi/6eb5a397e96abfdf9642 –  Apr 24 '15 at 01:41