javascriptjsond3.jsmapscartogram

How to remove lines in between cartogram Maps in d3.js


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.


Solution

  • 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