I am creating an interactive visualization tool with D3. I just got my map working today and I noticed that the first two features from the topojson are not loading in.
The first two entries of the topojson look like this and geojson.io reads these countries in fine. (They appear on the map)
"objects":{"ne_10m_admin_0_countries":{"type":"GeometryCollection","geometries":[{"arcs":[[[0,1]],[[2,3,4,5]],[[6,7]],[[8,9]],[[10]],[[11]],[[12]],[[13]],[[14]],[[15]],[[16]],[[17]],[[18]],[[19]],[[20]],[[21]],[[22]],[[23]],[[24]],[[25]],[[26]],[[27]],[[28]],[[29]],[[30]],[[31]],[[32]],[[33]],[[34]],[[35]],[[36]],[[37]],[[38]],[[39]],[[40]],[[41]],[[42]],[[43]],[[44]],[[45]],[[46]],[[47]],[[48]],[[49]],[[50]],[[51]],[[52]],[[53]],[[54]],[[55]],[[56]],[[57]],[[58]],[[59]],[[60]],[[61]],[[62]],[[63]],[[64]],[[65]],[[66]],[[67]],[[68]],[[69]],[[70]],[[71]],[[72]],[[73]],[[74]],[[75]],[[76]],[[77]],[[78]],[[79]],[[80]],[[81]],[[82]],[[83]],[[84]],[[85]],[[86]],[[87]],[[88]],[[89]],[[90]],[[91]],[[92]],[[93]],[[94]],[[95]],[[96]],[[97]],[[98]],[[99]],[[100]],[[101]],[[102]],[[103]],[[104]],[[105]],[[106]],[[107]],[[108]],[[109]],[[110]],[[111]],[[112]],[[113]],[[114]],[[115]],[[116]],[[117]],[[118]],[[119]],[[120]],[[121]],[[122]],[[123]],[[124]],[[125]],[[126]],[[127]],[[128]],[[129]],[[130]],[[131]]],"type":"MultiPolygon","properties":{"featurecla":"Admin-0 country","scalerank":5,"LABELRANK":2,"SOVEREIGNT":"Indonesia","SOV_A3":"IDN","ADM0_DIF":0,"LEVEL":2,"TYPE":"Sovereign country","ADMIN":"Indonesia","ADM0_A3":"IDN","GEOU_DIF":0,"GEOUNIT":"Indonesia","GU_A3":"IDN","SU_DIF":0,"SUBUNIT":"Indonesia","SU_A3":"IDN","BRK_DIFF":0,"NAME":"Indonesia","NAME_LONG":"Indonesia","BRK_A3":"IDN","BRK_NAME":"Indonesia","BRK_GROUP":"","ABBREV":"Indo.","POSTAL":"INDO","FORMAL_EN":"Republic of Indonesia","FORMAL_FR":"","NAME_CIAWF":"Indonesia","NOTE_ADM0":"","NOTE_BRK":"","NAME_SORT":"Indonesia","NAME_ALT":"","MAPCOLOR7":6,"MAPCOLOR8":6,"MAPCOLOR9":6,"MAPCOLOR13":11,"POP_EST":260580739,"POP_RANK":17,"GDP_MD_EST":3028000,"POP_YEAR":2017,"LASTCENSUS":2010,"GDP_YEAR":2016,"ECONOMY":"4. Emerging region: MIKT","INCOME_GRP":"4. Lower middle income","WIKIPEDIA":-99,"FIPS_10_":"ID","ISO_A2":"ID","ISO_A3":"IDN","ISO_A3_EH":"IDN","ISO_N3":"360","UN_A3":"360","WB_A2":"ID","WB_A3":"IDN","WOE_ID":23424846,"WOE_ID_EH":23424846,"WOE_NOTE":"Exact WOE match as country","ADM0_A3_IS":"IDN","ADM0_A3_US":"IDN","ADM0_A3_UN":-99,"ADM0_A3_WB":-99,"CONTINENT":"Asia","REGION_UN":"Asia","SUBREGION":"South-Eastern Asia","REGION_WB":"East Asia & Pacific","NAME_LEN":9,"LONG_LEN":9,"ABBREV_LEN":5,"TINY":-99,"HOMEPART":1,"MIN_ZOOM":0,"MIN_LABEL":1.7,"MAX_LABEL":6.7,"NE_ID":1159320845,"WIKIDATAID":"Q252","NAME_AR":"إندونيسيا","NAME_BN":"ইন্দোনেশিয়া","NAME_DE":"Indonesien","NAME_EN":"Indonesia","NAME_ES":"Indonesia","NAME_FR":"Indonésie","NAME_EL":"Ινδονησία","NAME_HI":"इंडोनेशिया","NAME_HU":"Indonézia","NAME_ID":"Indonesia","NAME_IT":"Indonesia","NAME_JA":"インドネシア","NAME_KO":"인도네시아","NAME_NL":"Indonesië","NAME_PL":"Indonezja","NAME_PT":"Indonésia","NAME_RU":"Индонезия","NAME_SV":"Indonesien","NAME_TR":"Endonezya","NAME_VI":"Indonesia","NAME_ZH":"印度尼西亚"}},{"arcs":[[[132,-1]],[[133,134]],[[135,-8,136,137,138,139]],[[140]],[[141]],[[142]],[[143]],[[144]],[[145]]],"type":"MultiPolygon","properties":{"featurecla":"Admin-0 country","scalerank":5,"LABELRANK":3,"SOVEREIGNT":"Malaysia","SOV_A3":"MYS","ADM0_DIF":0,"LEVEL":2,"TYPE":"Sovereign country","ADMIN":"Malaysia","ADM0_A3":"MYS","GEOU_DIF":0,"GEOUNIT":"Malaysia","GU_A3":"MYS","SU_DIF":0,"SUBUNIT":"Malaysia","SU_A3":"MYS","BRK_DIFF":0,"NAME":"Malaysia","NAME_LONG":"Malaysia","BRK_A3":"MYS","BRK_NAME":"Malaysia","BRK_GROUP":"","ABBREV":"Malay.","POSTAL":"MY","FORMAL_EN":"Malaysia","FORMAL_FR":"","NAME_CIAWF":"Malaysia","NOTE_ADM0":"","NOTE_BRK":"","NAME_SORT":"Malaysia","NAME_ALT":"","MAPCOLOR7":2,"MAPCOLOR8":4,"MAPCOLOR9":3,"MAPCOLOR13":6,"POP_EST":31381992,"POP_RANK":15,"GDP_MD_EST":863000,"POP_YEAR":2017,"LASTCENSUS":2010,"GDP_YEAR":2016,"ECONOMY":"6. Developing region","INCOME_GRP":"3. Upper middle income","WIKIPEDIA":-99,"FIPS_10_":"MY","ISO_A2":"MY","ISO_A3":"MYS","ISO_A3_EH":"MYS","ISO_N3":"458","UN_A3":"458","WB_A2":"MY","WB_A3":"MYS","WOE_ID":23424901,"WOE_ID_EH":23424901,"WOE_NOTE":"Exact WOE match as country","ADM0_A3_IS":"MYS","ADM0_A3_US":"MYS","ADM0_A3_UN":-99,"ADM0_A3_WB":-99,"CONTINENT":"Asia","REGION_UN":"Asia","SUBREGION":"South-Eastern Asia","REGION_WB":"East Asia & Pacific","NAME_LEN":8,"LONG_LEN":8,"ABBREV_LEN":6,"TINY":-99,"HOMEPART":1,"MIN_ZOOM":0,"MIN_LABEL":3,"MAX_LABEL":8,"NE_ID":1159321083,"WIKIDATAID":"Q833","NAME_AR":"ماليزيا","NAME_BN":"মালয়েশিয়া","NAME_DE":"Malaysia","NAME_EN":"Malaysia","NAME_ES":"Malasia","NAME_FR":"Malaisie","NAME_EL":"Μαλαισία","NAME_HI":"मलेशिया","NAME_HU":"Malajzia","NAME_ID":"Malaysia","NAME_IT":"Malesia","NAME_JA":"マレーシア","NAME_KO":"말레이시아","NAME_NL":"Maleisië","NAME_PL":"Malezja","NAME_PT":"Malásia","NAME_RU":"Малайзия","NAME_SV":"Malaysia","NAME_TR":"Malezya","NAME_VI":"Malaysia","NAME_ZH":"马来西亚"}},{"arcs":[[[146,147,148,149]],[[150,151,152,153]],[[154]],[[155]],[[156]],[[157]],[[158]],[[159]],[[160]],[[161]],[[162]],[[163]],[[164]],[[165]],[[166]],[[167]],[[168]],[[169]],[[170]],[[171]],[[172]],[[173]],[[174]],[[175]],[[176]],[[177]],[[178]],[[179]],[[180]],[[181]],[[182]],[[183]],[[184]],[[185]],[[186]],[[187]],[[188]],[[189]],[[190]],[[191]],[[192]],[[193]],[[194]],[[195]],[[196]],[[197]],[[198]],[[199]],[[200]],[[201]],[[202]],[[203]],[[204]],[[205]],[[206]],[[207]],[[208]],[[209]],[[210]],[[211]],[[212]],[[213]],[[214]],[[215]],[[216]],[[217]],[[218]],[[219]],[[220]],[[221]],[[222]],[[223]],[[224]],[[225]],[[226]],[[227]],[[228]],[[229]],[[230]],[[231]],[[232]],[[233]],[[234]],[[235]],[[236]],[[237]]],
Here is my JS:
<script>window.onload = setMap();
function setMap(){
d3.csv("/data/fdata.csv").then(function(data) {
//console.log(data);
d3.json("/data/a.topojson").then(function(data2) {
//console.log(data2);
var width = 960,
height = 460;
//create new svg container for the map
var map = d3.select("body")
.append("svg")
.attr("class", "map")
.attr("width", width)
.attr("height", height);
var projection = d3.geoNaturalEarth1()
.center([0, 0])
.rotate([-2, 0, 0])
//.parallels([43, 62])
.scale(175)
.translate([width / 2, height / 2]);
var path = d3.geoPath()
.projection(projection);
d3.selectAll(".boundary")
.style("stroke-width", 1 / 1);
var b = topojson.feature(data2, data2.objects.ne_10m_admin_0_countries);
var graticule = d3.geoGraticule();
var attrArray = ["x1","x2","x3" ];
function joinData(b, data){
for (var i=0; i<data.length; i++){
var csvRegion = data[i]; //the current region
var csvKey = data[i].Country; //the CSV primary key
for (var a=0; a<b.features.length; a++){
var geojsonProps = b.features[a].properties; //gj props
var geojsonKey = geojsonProps.ADMIN; //the geojson primary key
if (geojsonKey == csvKey){
attrArray.forEach(function(attr){
var val = parseFloat(csvRegion[attr]);
geojsonProps[attr] = val;
});
};
};
};
return b;
};
joinData(b,data);
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var currentF = "B2";
var color = d3.scaleQuantile()
.domain(d3.range(0, 1000))
.range(d3.schemeReds[7]);
map.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
map.append("path")
.datum(graticule.outline)
.attr("class", "graticule outline")
.attr("d", path);
map.selectAll("path")
.data(b.features)
.enter()
.append("path")
.attr("d", path)
//.style("stroke", "black")
.on("mouseover", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", .9)
.style("stroke-opacity", 1.0);
tooltip.html(d.properties.ADMIN)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0)
.style("stroke-opacity", 0);
})
.style("fill", function(d) { return color(d.properties[currentF]); });
}); //csv
}); //json
};
I believe the issue is with the map.selectAll("path") chunk. When I run it without D3 iterating through features it seems to come out fine. (With Indonesia and Malaysia still there. )
The issue is here:
map.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
map.append("path")
.datum(graticule.outline)
.attr("class", "graticule outline")
.attr("d", path);
map.selectAll("path")
.data(b.features)
.enter()
.append("path")
.attr("d", path)
The two append statements create paths, you then select these existing paths when you do: map.selectAll("path"). To confirm, before you append the features, you can do console.log(map.selectAll("path").size()) to see how many elements are selected, it should be two.
An enter selection can be used to create elements in the DOM for each item in the data array that does not have a corresponding element in the DOM.
As there are two paths in your selection, the first two items in the data array correspond to these two elements, and thus do not need to be entered: they already exist (By binding the data with .data() you are binding the first two datums in the data array to the already existing paths). D3 doesn't "know" that these paths aren't supposed to be affiliated with those data array items.
In order to ensure all items are entered you can make sure that you have an empty selection before you bind data:
map.selectAll(null).data()...
map.selectAll().data()...
map.selectAll(".className").data()... // where no element has that class yet
I'm trying to plot some lat/long points onto a map, but I can't get them to appear in the correct place.
The dots should be in San Francisco. I have a JSfiddle of the code.
var width = 400,
height = 400;
var projection = d3.geo.azimuthalEqualArea()
.clipAngle(180 - 1e-3)
.scale(100)
.translate([width / 2, height / 2])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("defs").append("path")
.datum({type: "Sphere"})
.attr("id", "sphere")
.attr("d", path);
svg.append("use")
.attr("class", "stroke")
.attr("xlink:href", "#sphere");
svg.append("use")
.attr("class", "fill")
.attr("xlink:href", "#sphere");
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
d3.json("http://bl.ocks.org/mbostock/raw/4090846/world-50m.json", function(error, world) {
svg.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
svg.insert("path", ".graticule")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "boundary")
.attr("d", path);
});
var latlong = {"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838312,-122.0423922]},"properties":{"timestampMs":1415894666875}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838474,-122.0423972]},"properties":{"timestampMs":1415894601718}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838474,-122.0423972]},"properties":{"timestampMs":1415894536288}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838411,-122.0424015]},"properties":{"timestampMs":1415894471356}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.383878,-122.0423925]},"properties":{"timestampMs":1415894406257}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838317,-122.0423856]},"properties":{"timestampMs":1415894326769}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838287,-122.0423933]},"properties":{"timestampMs":1415894261810}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.383829,-122.0423847]},"properties":{"timestampMs":1415894196224}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838765,-122.0424141]},"properties":{"timestampMs":1415894131768}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838177,-122.0423668]},"properties":{"timestampMs":1415894066809}}]};
// THESE ARE THE POINTS THAT ARE NOT BEING PLACED CORRECTLY
svg.selectAll("circle")
.data(latlong.features).enter()
.append("circle")
.attr("cx", function (d) { return projection(d.geometry.coordinates)[1]; })
.attr("cy", function (d) { return projection(d.geometry.coordinates)[0]; })
.attr("r", "2px")
.attr("fill", "red");
You've specified the latitude and longitude the wrong way round for d3.geo and you're also taking the output the wrong way round. It is counter to the way that they are displayed by convention (N/S then E/W) but it is more consistent with a drawing convention of across then up/down.
From path.projection in the D3 API Geo reference:
A projection function takes a two-element array of numbers
representing the coordinates of a location, [longitude, latitude], and
returns a similar two-element array of numbers representing the
projected pixel position [x, y].
To fix this, I've reversed the coordinates of your FeatureCollection:
var latlong = {"type":"FeatureCollection","features":[
{"type":"Feature","geometry":{"type":"Point","coordinates":[-122.0423922,37.3838312]},"properties":{"timestampMs":1415894666875}},
etc...
and reversed the coordinates of your plot.
.attr("cx", function (d) { return projection(d.geometry.coordinates)[0]; })
.attr("cy", function (d) { return projection(d.geometry.coordinates)[1]; })
Everything else was fine. Amended JSFiddle here. So often it's the little things!
I've created a little test line chart using D3, but since I am quite new to the library I am not sure what the best way would be to add multiple lines to a chart, at the moment I only have one line displayed in this fiddle.
I would like to display 2 lines on the chart, but I am unsure of how to achieve that without copy pasting code, which I am sure would be very inefficient as I would like to update/animate the graph at regular intervals based on user selection.
Instead of this,
var data = [12345,22345,32345,42345,52345,62345,72345,82345,92345,102345,112345,122345,132345,142345];
I would like to display something like this,
var data = [
[12345,42345,3234,22345,72345,62345,32345,92345,52345,22345], // line one
[1234,4234,3234,2234,7234,6234,3234,9234,5234,2234] // line two
];
Would this be a possibility? If so, what would be the best way to approach this, so that I can easily update/animate the graph when needed?
Note: I am merely trying to learn and to familiarize myself with D3 best practices and the library as a whole. Thanks.
This is possible and reasonable.
There is a tutorial that approaches this at the
D3 Nested Selection Tutorial
which describes nesting of data.
Below is code that I hacked from your fiddle to demonstrate this.
var data = [
[12345,42345,3234,22345,72345,62345,32345,92345,52345,22345],
[1234,4234,3234,2234,7234,6234,3234,9234,5234,2234]
];
var width = 625,
height = 350;
var x = d3.scale.linear()
.domain([0,data[0].length]) // Hack. Computing x-domain from 1st array
.range([0, width]);
var y = d3.scale.linear()
.domain([0,d3.max(data[0])]) // Hack. Computing y-domain from 1st array
.range([height, 0]);
var line = d3.svg.line()
.x(function(d,i) { return x(i); })
.y(function(d) { return y(d); });
var area = d3.svg.area()
.x(line.x())
.y1(line.y())
.y0(y(0));
var svg = d3.select("body").append("svg")
//.datum(data)
.attr("width", width)
.attr("height", height)
//.append("g");
var lines = svg.selectAll( "g" )
.data( data ); // The data here is two arrays
// for each array, create a 'g' line container
var aLineContainer = lines
.enter().append("g");
/*svg.append("path")
.attr("class", "area")
.attr("d", area);*/
aLineContainer.append("path")
.attr("class", "area")
.attr("d", area);
/*svg.append("path")
.attr("class", "line")
.attr("d", line);*/
aLineContainer.append("path")
.attr("class", "line")
.attr("d", line);
/*svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", line.x())
.attr("cy", line.y())
.attr("r", 3.5);*/
// Access the nested data, which are values within the array here
aLineContainer.selectAll(".dot")
.data( function( d, i ) { return d; } ) // This is the nested data call
.enter()
.append("circle")
.attr("class", "dot")
.attr("cx", line.x())
.attr("cy", line.y())
.attr("r", 3.5);
One deficiency is that I computed the domain for the x and y axes off the first array, which is a hack, but not pertinent to your question exactly.