Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3 plotting longitude/latitude of null projection

The d3.geo.path has a null projection because the TopoJSON is already projected, so it can be displayed as-is. I'm trying to plot data in the form of [longitude, latitude] on a map.

Below is what my code basically looks like:

var width, height, path, svg;

width = 960;
height = 600;
path = d3.geo.path().projection(null);
svg = d3.select('.viz').append('svg')
    .attr('width', width)
    .attr('height', height);

d3.json("counties.json", function(error, us) {
    svg.append('path')
        .datum(topojson.mesh(us))
        .attr('d', path);
});

svg.selectAll('.pin')
    .data(ds) // i.e. ds = {[12.521, 15.312], [616.122,-31.160]}
    .enter().append('circle', '.pin')
    .attr('r', 3)
    .attr('transform', function (d) {
        return 'translate(' + path([
            d.longitude,
            d.latitude
        ]) + ')';
    });

I debugged through this, and I do get the data fine. But, I am getting an error that "path([d.longitude, d.latitude])" is undefined. "d" exists with longitude and latitude values. "path" also exists. I think it's because the projection is null.

What can I do to make this work?

------- EDIT ------- I followed the recommendation by Ben Lyall to remove the use of "path" in the selectAll statement as well as moving the selectAll statement inside the .json(). I also noticed I put in an incorrect example for ds, so I modified the comment. Here is my updated code.

This displays the map fine with no console errors, but I still don't see any circles in the map itself.

var width, height, path, svg;

width = 960;
height = 600;
path = d3.geo.path().projection(null);
svg = d3.select('.viz').append('svg')
    .attr('width', width)
    .attr('height', height);

d3.json("counties.json", function(error, us) {
    svg.append('path')
        .datum(topojson.mesh(us))
        .attr('d', path);

    svg.selectAll('.pin')
        .data(ds) // i.e. ds = [{longitude: 12.521, latitude: 15.312}, {longitude: 616.122, latitude: -31.160}]
        .enter().append('circle', '.pin')
        .attr('r', 3)
        .attr('transform', function (d) {
            return 'translate(' +
                d.longitude + ',' + d.latitude +
            ')';
        });
});

------- EDIT ------- The solution was a combination of Ben Lyall's proposed solution as well as taking into account the projection already done on the map for the .pins. Since the projection in code is null, I had to create a new one that matches the projection for the map and then use that when performing the transform on the .pin's.

Below is the solution:

var width, height, path, projection, svg;

width = 960;
height = 600;
path = d3.geo.path().projection(null);
projection = d3.geo.albersUsa().scale(1280).translate([width/2, height/2]);
svg = d3.select('.viz').append('svg')
    .attr('width', width)
    .attr('height', height);

d3.json("counties.json", function(error, us) {
    svg.append('path')
        .datum(topojson.mesh(us))
        .attr('d', path);

    svg.selectAll('.pin')
        .data(ds)
        .enter().append('circle', '.pin')
        .attr('r', 3)
        .attr('transform', function (d) {
            return 'translate(' +
                projection([d.longitude, d.latitude]) +
            ')';
        });
});
like image 876
boombox Avatar asked Dec 29 '25 02:12

boombox


1 Answers

When you're positioning your .pin elements, why are you using path in your translate? You don't want to create a path, and the d.latitude and d.longitude values should already be in pixel co-ordinates, since they're projected already, so you should be able to use them directly.

Note: you probably also want that part of your code inside the d3.json handler, rather than outside, since it'll run asynchronously before your data is set to anything (this is probably the actual issue with your code, rather than using path incorrectly).

It's a little hard to confirm without an example to fork, but try this:

var width, height, path, svg;

width = 960;
height = 600;
path = d3.geo.path().projection(null);
svg = d3.select('.viz').append('svg')
    .attr('width', width)
    .attr('height', height);

d3.json("counties.json", function(error, us) {
    svg.append('path')
        .datum(topojson.mesh(us))
        .attr('d', path);

    svg.selectAll('.pin')
        .data(ds) // i.e. ds = {[12.521, 15.312], [616.122, -31.160]}
        .enter().append('circle', '.pin')
        .attr('r', 3)
        .attr('transform', function (d) { 
            return 'translate(' + d.longitude + "," + d.latitude + ')';
        });
});
like image 56
Ben Lyall Avatar answered Dec 30 '25 15:12

Ben Lyall



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!