hobbes3
hobbes3

Reputation: 30228

How to apply topojson's projection to a voronoi in d3?

I've noticed that straight lines are curved to the projection, but the Voronoi regions are in straight lines.

enter image description here

Is it possible to "force" the Voronoi region to also be curved and applied to the topojson's projection like the straight lines and lat/lon coordinates?

The original map: http://bl.ocks.org/mbostock/7608400

A section of my code:

var projection = d3.geo.kavrayskiy7()
    .center([center_lon, center_lat])
    .scale(zoom)
    .translate([width / 2, height / 2])

var graticule = d3.geo.graticule();

var path = d3.geo.path()
    .projection(projection);

var voronoi = d3.geom.voronoi()
    .x(function(d) { return d.x; })
    .y(function(d) { return d.y; })
    .clipExtent([[0, 0], [width, height]]);

var svg = d3.select(that.el).append("svg")
    .attr("width", width)
    .attr("height", height);

svg.append("path")
    .datum(graticule)
    .attr("class", "graticule")
    .attr("d", path);

svg.append("path")
    .datum(graticule.outline)
    .attr("class", "graticule outline")
    .attr("d", path);

d3.json("/static/app/custom_vizs/components/voronoi/readme-world.json", function(error, world) {
    var countries = topojson.feature(world, world.objects.countries).features,
        neighbors = topojson.neighbors(world.objects.countries.geometries);

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

    var format = d3.format(",");

    var get_points_by_id = d3.map(),
        positions = [];

    var src = _(data).chain().groupBy(src_field).each(function(v, k, o) { o[k] = v; }).value();
    var dst = _(data).chain().groupBy(dst_field).each(function(v, k, o) { o[k] = v; }).value();

    var uniques = _(dst).extend(src);

    var max = 0;

    var points = _(uniques).map(function(v, k) {
        var o = {};

        o.id = k;
        o.value = _(v).pluck(count_field).reduce(function(memo, num) { return memo + parseFloat(num); }, 0);

        max = Math.max(max, o.value);

        if(v[0][src_field] === k) {
            o.lat = v[0][src_lat_field];
            o.lon = v[0][src_lon_field];
        }
        else {
            o.lat = v[0][dst_lat_field];
            o.lon = v[0][dst_lon_field];
        }

        return o;
    });

    points.forEach(function(d) {
        get_points_by_id.set(d.id, d);
        d.outgoing = [];
        d.incoming = [];
    });

    data.forEach(function(connection) {
        var source = get_points_by_id.get(connection[src_field]),
            target = get_points_by_id.get(connection[dst_field]),
            link = {source: source, target: target};
        source.outgoing.push(link);
        target.incoming.push(link);
    });

    points = points.filter(function(d) {
        if (d.count = Math.max(d.incoming.length, d.outgoing.length)) {
        d[0] = +d.lon;
        d[1] = +d.lat;
        var position = projection(d);
        d.x = position[0];
        d.y = position[1];
        return true;
        }
    });

    voronoi(points)
        .forEach(function(d) { d.point.cell = d; });

    var point = svg.append("g")
        .attr("class", "points")
        .selectAll("g")
        .data(points.sort(function(a, b) { return b[count_field] - a[count_field]; }))
        .enter().append("g")
        .attr("class", "point");

    point.append("path")
        .attr("class", "point-cell")
        .attr("d", function(d) { return d.cell.length ? "M" + d.cell.join("L") + "Z" : null; });

    point.append("g")
        .attr("class", "point-arcs")
        .selectAll("path")
        .data(function(d) { return d.outgoing; })
        .enter().append("path")
        .attr("d", function(d) { return path({type: "LineString", coordinates: [d.source, d.target]}); });

    point.append("circle")
        .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
        .attr("r", function(d, i) { return d.value/max*max_circle_size; });

    point.append("title")
        .text(function(d) {
            return d.id + ": " +
                format(d.outgoing.length) + " distinct outgoing, " +
                format(d.incoming.length) + " distinct incoming, " +
                format(d.value) + " total";
        });
});

Upvotes: 0

Views: 549

Answers (1)

Cybercartel
Cybercartel

Reputation: 12592

I don't think so. The graticules (curved lines) has a start and end point. How would you define that for the voronoi diagram? Maybe for each cell? I don't think it would work.

Upvotes: 1

Related Questions