3

I'm using topojson to draw a world map with D3js. I believe this is working fine. However, when I try and project latitude/longitude points onto the map, the projection function is returning "NaN" for points with negative values, and the projection for points that are working is way off the country's position on the world map.

This is the data I'm reading in: assets/data/gcf.csv

Coords,Country,Frequency
"[38.9597594, 34.9249653]",Turkey,1
"[61.0666922, -107.9917071]",Canada,3
"[52.5001698, 5.7480821]",Netherlands,1
"[42.6384261, 12.674297]",Italy,1
"[39.7837304, -100.4458825]",United States,69
"[59.6749712, 14.5208584]",Sweden,1
"[35.000074, 104.999927]",China,5
"[64.6863136, 97.7453061]",Russian Federation,2
"[36.5748441, 139.2394179]",Japan,1

Index.html

<div id="worldmap"></div>
<script src="../assets/js/graphs/map.js"></script>

assets/js/graphs/map.js

var width = 960;
var height = 500;

// set projection
var projection = d3.geo.mercator();

// create path variable
var path = d3.geo.path()
.projection(projection); 

// Define the div for the tooltip
var div = d3.select("body").append("div")   
.attr("class", "tooltip")               
.style("opacity", 0);

d3.json("https://unpkg.com/world-atlas@1/world/110m.json", function(error1, topo) {
if(error1) console.log("Error: topo json not loaded.");
d3.csv("../assets/data/gcf.csv", function(error2, data) {
if(error2) console.log("Error: Coord/frequency data not loaded.");

countries = topojson.feature(topo, topo.objects.countries).features;

projection
.scale(width / 2 / Math.PI)
.translate([(width / 2)-30, (height / 2)+50]);

data.forEach(function(d) {      
// + symbol convert from string representation of a number to an actual number
d.Frequency = +d.Frequency;
d.Coords = d.Coords.replace(/[\[\]"]+/g, '');
d.Coords = d.Coords.split(',');
d.Coords = d.Coords.map(x => parseFloat(x));
d.Country = d.Country;
console.log("Country:", d.Country, ". Projection: ", projection(d.Coords)[0], projection(d.Coords)[1]);
});

// create svg variable
var svg = d3.select("#worldmap").append("svg")
.attr("width", width)
.attr("height", height);

svg.append("rect")
.attr("width", "100%")
.attr("height","100%")
.attr("fill","#A2E8E8");

// add countries from topojson
svg.selectAll("path")
.data(countries).enter()
.append("path")
.attr("class", "feature")
.style("fill", "#71945A")
.attr("d", path);

// put boarder around countries 
svg.append("path")
.datum(topojson.mesh(topo, topo.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "mesh")
.attr("d", path);
aa = [38.9597594, 34.9249653];


// add circles to svg
svg.selectAll("circle")
.data(data).enter()
.append("circle")
.attr("r", function(d) {
return d.Frequency === 1 ? d.Frequency* 10 : d.Frequency / 2
})
.attr("transform", function(d) {
return "translate(" + projection([
  (d.Coords[0]),
  (d.Coords[1])
]) + ")";
})
.attr("fill", "red")
.on("mouseover", function(d) {      
div.transition()        
.duration(200)      
.style("opacity", .9);      
div .html(d.Country)    
.style("left", (d3.event.pageX) + "px")     
.style("top", (d3.event.pageY - 28) + "px");    
})                  
.on("mouseout", function(d) {       
div.transition()        
.duration(500)      
.style("opacity", 0);   
});         
});
});

Styles for world map:

.feature {
fill: none;
stroke: grey;
stroke-width: 1px;
stroke-linejoin: round;
}

.mesh {
fill: none;
stroke: black;
stroke-width: 2px;
stroke-linejoin: round;
}

1 Answers1

2

Your latitude and longitude are reversed - a d3 geoProjection takes coordinates in an [x,y] or [long,lat] format. You are providing the projection with an array of [lat,long], but if you switch the code below:

.attr("transform", function(d) { 
    return "translate(" + projection([d.Coords[0],d.Coords[1]]) + ")";
})

To:

.attr("transform", function(d) { 
    return "translate(" + projection([d.Coords[1],d.Coords[0]]) + ")";
})

You'll get the map you are looking for:

enter image description here

Andrew Reid
  • 37,021
  • 7
  • 64
  • 83